/*
Copyright (c) [2019年5月1日] [吴超]
[MBT_Studio] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
		 http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/
#include "stdafx.h"
#include "ui节点视口.h"

#include <omp.h>
#include <随机数.h>
#include <字符串数值互换.h>

#include <core/geometry.h>
#include <core/几何计算/凸包计算.h>

#include <场景/intern/S_Scene.h>

#include <异步解算/物理异步解算.h>
#include <节点/输入输出节点.h>

#include "框架/工作区/节点视口/ui节点创建菜单.h"
#include "框架/工作区/通用弹出菜单.h"

#include "物体编辑/3D控制手柄.h"
//#include "视口元素计算/线操作计算.h"
#include "拖放数据创建节点.h"
#include "节点视口工作区.h"

#include "框架/工作区/工作区.h"



typedef struct {
	uvec2     顶点区间;
	S_RGBA8UI 区颜色;
}S_凸包顶点区间和颜色;



static float32			g链接线圆角最大半径 = 20;
static float32			g插座空白宽度 = 2;



//DEF_文本绘制ID()
#define DEF_剪刀线状态_准备 0B00000001
#define DEF_剪刀线状态_开始 0B00000010
#define DEF_剪刀线状态_添加 0B00000100
#define DEF_剪刀线状态_结束 0B00000000
static uint8 g剪刀线状态_状态 = DEF_剪刀线状态_结束;


static vec2			g纵向排列左边插座大小 = { 4,8 };
static vec2			g纵向排列插座大小 = { 12, 12 };
static uint32		g插座文本最大数 = 16;

static uint8 字符跨度 = 32;

static bool g开始移动视口			= false;
static bool g移动选中节点			= false;
static bool g打开选框绘制			= false;
static vec2 g节点鼠标局部位置		= { 0,0 };
static vec2 g节点视口鼠标局部位置	= { 0,0 };
static uint32 g每个面板元素数量		= 5;

static float32					g节点面板圆角半径	= 6;
static uint32					g当前节点添加到树_树类型标示 = 0xffffffff;


static C_节点操作_移动节点* g当前移动面板操作 = nullptr;
static C_节点操作_删除节点* g当前删除面板操作 = nullptr;

static C_节点视口*			g当前操作节点视口	= nullptr;

C_Widget* C_节点视口::g多边形节点创建菜单 = nullptr;
C_Widget* C_节点视口::g函数节点创建菜单 = nullptr;
C_Widget* C_节点视口::g物体节点创建菜单 = nullptr;
C_Widget* C_节点视口::g材质节点创建菜单 = nullptr;
C_Widget* C_节点视口::g渲染节点创建菜单 = nullptr;
C_Widget* C_节点视口::g着色器节点创建菜单 = nullptr;
C_Widget* C_节点视口::g几何着色节点创建菜单 = nullptr;
C_Widget* C_节点视口::g并行节点创建菜单 = nullptr;
C_Widget* C_节点视口::g节点层级切换面板 = nullptr;


static std::map<C_节点树*, std::set<C_节点视口*>>	g节点视口;
static std::map<C_插座基类*, S_节点添加结构>		g节点添加结构;
static std::set<C_节点树*>							g返回菜单以记录节点树;




static uint32 f_收集节点凸包顶点(C_节点视口* 视口, C_节点面板* panel, std::vector<mat2x4>& 顶点, std::vector<S_凸包顶点区间和颜色>& 凸包顶点区间) {
	S_凸包顶点区间和颜色 区间;
	区间.顶点区间.x = 顶点.size()*4;
	区间.顶点区间.y = (panel->m_NodeData->m_区域内节点.size() + 1) * 4;
	

	mat2x4 point;
	f_NodePanel_get边界框(panel, point);
	顶点.push_back(point);

	if (panel->m_NodeData->m_区域内节点.size()) {
		for (auto& node : panel->m_NodeData->m_区域内节点) {
			for (auto& 区域内面板 : node->m_包含在多个视口的UI部件) {
				if (区域内面板->m_父部件 == 视口) {
					f_NodePanel_get边界框((C_节点面板*)区域内面板, point);
					顶点.push_back(point);
					//区间.顶点区间.y += 4;
					break;
				}
			}
		}

		for (auto& node : panel->m_NodeData->m_区域内节点) {
			for (auto& 区域内面板 : node->m_包含在多个视口的UI部件) {
				if (区域内面板->m_父部件 == 视口) {
					switch (node->m_Type) {
						case E_节点类型::e_节点Type_循环迭代:
						case E_节点类型::e_节点Type_判断:
						case E_节点类型::e_节点Type_迭代:
						case E_节点类型::e_节点Type_循环: {
							区间.顶点区间.y += f_收集节点凸包顶点(视口, (C_节点面板*)区域内面板, 顶点, 凸包顶点区间);
							break;
						}
					}
					break;
				}
			}
		}

	}
	
	switch (panel->m_NodeData->m_Type) {
		case E_节点类型::e_节点Type_判断: 
			区间.区颜色 = { 0, 150, 200, 10 }; 
			break;
		case E_节点类型::e_节点Type_循环迭代:
		case E_节点类型::e_节点Type_迭代:
		case E_节点类型::e_节点Type_循环:
			区间.区颜色 = { 220, 120, 0, 10 }; 
			break;
	}
	凸包顶点区间.push_back({区间});

	return 区间.顶点区间.y;
}

static void f_设置当前操作节点视口(C_节点视口* view) {
	//g当前操作节点视口 = view;

	switch (view->m_节点树后端->m_树类型) {
		case E_节点树类型::e_type_物体节点树:
			S_节点数据::G当前物体节点树 = view->m_节点树后端;
			break;
		case E_节点树类型::e_type_材质节点树: {
			S_节点数据::G当前材质节点树 = view->m_节点树后端;
			break;
		}
		case E_节点树类型::e_type_几何着色节点树:
		case E_节点树类型::e_type_着色节点树: {
			S_节点数据::G当前材质节点树 = view->m_节点树后端;
			break;
		}
		case E_节点树类型::e_type_并行节点树: {
			S_节点数据::G当前并行节点树 = view->m_节点树后端;
			break;
		}
		case E_节点树类型::e_type_渲染节点树: {
			S_节点数据::G当前渲染节点树 = view->m_节点树后端;
			break;
		}
		case E_节点树类型::e_type_包节点树: {
			S_节点数据::G当前包节点树 = view->m_节点树后端;
			break;
		}
	}
}


/*************************************** 焦点处理 ********************************************/
static void f_节点获取焦点(C_节点面板** nodes, uint32 num) {
	//====================== 数据保存 ============================
	f_projectFile_添加焦点更新节点(nodes, num);
	

	std::vector<C_节点基类*> 节点面板集;
	for (uint32 i = 0; i < num; ++i) {
		for (auto& node : nodes[i]->m_NodeData->m_包含在多个视口的UI部件) {
			static_cast<C_节点面板*>(node)->f_设置焦点(true);
		}
		节点面板集.push_back(nodes[i]->m_NodeData);
	}

	
	f_global_clear激活节点();
	f_global_set激活物体(节点面板集);
	f_global_set激活节点(节点面板集);

	for (uint32 i = 0; i < num; ++i) {
		C_节点视口* view = static_cast<C_节点视口*>(nodes[i]->m_节点当前所在视口);
		nodes[i]->m_属性面板参数构建(view->m_属性栏, (S_结构指针)nodes[i]);
		f_widget_单个部件裁剪(nodes[i], view->m_裁剪大小, view->m_GlobalLoc);
	}


	for (auto& camera : S_框架::g_视口相机) {
		f_camera_set偏移视点(*camera, f_global_get激活物体包围中心点());
	}
	
	f_工作区_联动更新(E_工作区类型::e_节点视口, E_工作区操作类型::e_选择节点);

}



/***************************** 插座 ***************************/

static Inline vec2 f_node_get输入插座文本坐标(C_节点面板* plane, uint32 插座ID, uint16 字号) {
	vec2 coord = plane->f_getOriginalPos();
	coord.x += plane->f_getInSocket(插座ID)->f_getOriginalPos().x;
	
	coord.y += plane->f_getOriginalSize().y * 0.5;
	coord.y -= plane->m_内部面板->f_getOriginalSize().y * 0.5;

	coord.y -= C_节点面板::m_链接点直径 + 2;
	coord.y -= 字号 / plane->m_Scale.x;

	return coord;
}

static Inline vec2 f_node_get输出插座文本坐标(C_节点面板* plane, uint32 插座ID) {
	vec2 coord = plane->f_getOriginalPos();
	coord.x += plane->f_getOutSocket(插座ID)->f_getOriginalPos().x;
	coord.y += plane->f_getOutSocket(插座ID)->f_getOriginalPos().y;

	coord.y += C_节点面板::m_链接点直径 + 2;

	return coord;
}




static void f_节点缩放(C_节点视口* 节点视口) {
	if (!节点视口->m_节点树后端) return;

	//f_Widget_计算偏移比例(节点视口);


	auto ws = 节点视口->mf_get更新组件(节点视口);

	uint8 分批数量 = 4;
	uint32 分组数量 = ws.m_Num / 分批数量;
	if (分组数量) {
		omp_set_num_threads(DEF_Min(分组数量, 分批数量));
#pragma omp parallel for
		for (int32 i = 0; i < 分组数量; ++i) {
			uint32 index = i * 分批数量;

			auto* w = ws.m_W[index + 0];
			w->f_setSize(w->f_getOriginalSize() * 节点视口->m_扩展视口属性->m_比例);

			f_NodePanel_更新布局((C_节点面板*)w);
			f_widget_单个部件裁剪((C_节点面板*)w, 节点视口->m_裁剪大小, 节点视口->m_GlobalLoc);


			w = ws.m_W[index + 1];
			w->f_setSize(w->f_getOriginalSize() * 节点视口->m_扩展视口属性->m_比例);

			f_NodePanel_更新布局((C_节点面板*)w);
			f_widget_单个部件裁剪((C_节点面板*)w, 节点视口->m_裁剪大小, 节点视口->m_GlobalLoc);


			w = ws.m_W[index + 2];
			w->f_setSize(w->f_getOriginalSize() * 节点视口->m_扩展视口属性->m_比例);

			f_NodePanel_更新布局((C_节点面板*)w);
			f_widget_单个部件裁剪((C_节点面板*)w, 节点视口->m_裁剪大小, 节点视口->m_GlobalLoc);


			w = ws.m_W[index + 3];
			w->f_setSize(w->f_getOriginalSize() * 节点视口->m_扩展视口属性->m_比例);

			f_NodePanel_更新布局((C_节点面板*)w);
			f_widget_单个部件裁剪((C_节点面板*)w, 节点视口->m_裁剪大小, 节点视口->m_GlobalLoc);
		}
	}

	for (int32 i = 0; i < ws.m_Num - (分组数量 * 分批数量); ++i) {
		ws.m_W[i]->m_Scale = 节点视口->m_扩展视口属性->m_比例;
		ws.m_W[i]->f_setSize(ws.m_W[i]->f_getOriginalSize() * 节点视口->m_扩展视口属性->m_比例);

		f_NodePanel_更新布局((C_节点面板*)ws.m_W[i]);
		f_widget_单个部件裁剪((C_节点面板*)ws.m_W[i], 节点视口->m_裁剪大小, 节点视口->m_GlobalLoc);
	}

	节点视口->f_Scale();

}



void f_NodeView_更新节点(C_节点视口* view) {
	for (auto& e : view->m_焦点节点面板) {
		e->m_NodeData;
	}
}

vec2 f_NodeView_全局坐标转节点坐标(C_节点视口* view, vec2 全局坐标) {
	return (全局坐标 - view->m_扩展视口属性->m_中心) / view->m_扩展视口属性->m_比例;
}


std::vector<C_节点面板*> f_NodeView_拷贝节点(C_节点视口* view, std::vector<C_节点基类*>& 节点面板, bool 拷贝节点树) {
	view->f_清除所有节点面板();

	std::vector<C_节点基类*> 新节点 = f_node_深拷贝(节点面板.data(), 节点面板.size(), 拷贝节点树);

	std::vector<C_节点面板*> 新面板;
	uint32 num = 节点面板.size();
	for (uint32 i = 0; i < num; ++i) {
		auto* 节点面板 = view->mf_以节点构建节点(新节点[i], !拷贝节点树, false);

		f_NodeView_添加节点面板到视口(view, 节点面板, true);
		新面板.push_back(节点面板);

		新节点[i]->f_更新上层节点(true);
	}

	if (新面板.size()) {
		f_NodeView_添加激活节点(新面板, true);
	}

	return 新面板;
}

C_节点树* f_NodeView_取以前操作节点树() {
	return S_节点数据::G当前物体节点树;
}

void f_NodeView_添加激活节点(std::vector<C_节点面板*>& nodes, bool 是否清除以前) {
	if (是否清除以前) {
		for (auto& e : nodes) {
			if(!e->m_节点当前所在视口) continue;
			static_cast<C_节点视口*>(e->m_节点当前所在视口)->f_清除所有节点面板();
		}
	}

	for (auto& e : nodes) {
		if (!e->m_节点当前所在视口) continue;
		static_cast<C_节点视口*>(e->m_节点当前所在视口)->m_焦点节点面板.insert(e);
	}

	f_节点获取焦点(nodes.data(), nodes.size());
}

void f_NodeView_设置节点视口激活节点(std::vector<C_节点基类*>& nodes) {
	for (auto& node : nodes) {
		for (auto* plane : node->m_包含在多个视口的UI部件) {
			
			C_节点面板* 节点面板 = ((C_节点面板*)plane);
			if (节点面板->m_是否已弃用 == false) {
				((C_节点视口*)(节点面板->m_节点当前所在视口))->m_焦点节点面板.insert(节点面板);

				节点面板->m_更新绘制属性 = true;
				节点面板->f_设置焦点(true);
			}
		}
	}
}

void f_NodeView_清除所有视口中的节点() {
	for (auto& 节点视口组 : g节点视口) {
		for (auto& view : 节点视口组.second) {
			//view->f_清除所有节点面板();
		}
	}

	g节点视口.clear();
}








/****************************************** 视口输入事件 *******************************************/
static E_事件是否传递 on_节点视口鼠标按下(C_Widget* self, const E_鼠标按钮& 按钮) {
	C_节点视口* 视口 = dynamic_cast<C_节点视口*>(self);

	if (按钮 == E_鼠标按钮::e_鼠标左键) {
		if (g剪刀线状态_状态 & DEF_剪刀线状态_准备) {
			g剪刀线状态_状态 |= DEF_剪刀线状态_开始;
		}
		

		g当前移动面板操作 = nullptr;
	}
	else if (按钮 == E_鼠标按钮::e_鼠标中键) {
		//g开始移动视口 = true;
	}

	return E_事件是否传递::e_事件传递_终止;
}


static E_事件是否传递 on_节点视口鼠标放开(C_Widget* self, const E_鼠标按钮& 按钮) {
	C_节点视口* 视口 = dynamic_cast<C_节点视口*>(self);
	//return false;
	if (按钮 == E_鼠标按钮::e_鼠标左键) {
		//========================= 剪断连接线 ===========================
		if (g剪刀线状态_状态 & DEF_剪刀线状态_准备) {
			//std::map<C_插座基类*, S_节点添加结构> 节点添加结构;
			g节点添加结构.clear();

			//操作记录
			auto e = self->f_get更新组件();
			for (uint32 i = 0; i < e.m_Num; ++i) {
				C_节点面板* 节点面板 = (C_节点面板*)(e.m_W[i]);
				vec2 面板大小 = 节点面板->m_内部面板->f_getOriginalSize() * 0.4;

				
				节点面板->m_属性面板参数构建(视口->m_属性栏, (S_结构指针)节点面板);
			}


			g剪刀线状态_状态 ^= DEF_剪刀线状态_开始;
			f_NodeCtx_clear选中编辑物体();


			g当前操作节点视口 = 视口;
		}

		if (g移动选中节点) {
			f_NodeView_节点添加到框(视口, E_键位执行状态::e_完成);
			f_NodeView_节点相交链接(视口, E_键位执行状态::e_完成);
			f_NodeView_节点移动对齐(视口, E_键位执行状态::e_完成);
		}
		
		g移动选中节点 = false;
	}
	else if (按钮 == E_鼠标按钮::e_鼠标中键) {
		//if (g开始移动视口) {
		//	for (auto& e : 视口->m_焦点节点面板) {
		//		e->m_更新绘制属性 = true;
		//	}
		//	视口->m_更新绘制属性 = true;
		//
		//	
		//}
		//g开始移动视口 = false;

	}
	else if (按钮 == E_鼠标按钮::e_鼠标右键) {
		视口->m_焦点节点面板.clear();
	}

	g当前移动面板操作 = nullptr;
	C_节点面板::m_移动面板 = false;
	S_框架::g_开启节点移动 = true;

	f_projectFile_设置文件保存状态(false);
	f_NodeCtx_get默认场景()->m_光追后端->f_重置渲染帧记录();
	return E_事件是否传递::e_事件传递_终止;
}




static E_事件是否传递 on_节点视口鼠标移动(C_Widget* self, const ivec4& mouse) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);
	if (!节点视口->m_节点树后端) return E_事件是否传递::e_事件传递_终止;

	vec2 globalLoc{ float32(mouse.x), float32(mouse.y)};
	
	//if (g开始移动视口) {
	//	f_Widget_视口偏移(节点视口, C_Widget::g_鼠标.abs.x, C_Widget::g_鼠标.abs.y);
	//	
	//	f_节点缩放(节点视口);
	//	//f_节点平移(节点视口);
	//	f_projectFile_设置文件保存状态(false);
	//}
	//else 
	
	if (g移动选中节点) {
		vec2 鼠标位置 = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, 节点视口);
		uint32 i = 0;
		if (g当前移动面板操作) {
			g当前移动面板操作->f_移动(f_NodePanel_get标准坐标(鼠标位置, 节点视口));
			
		}
		f_projectFile_设置文件保存状态(false);
	}
	else {
		if (g剪刀线状态_状态 & DEF_剪刀线状态_开始 && g剪刀线状态_状态 & DEF_剪刀线状态_准备) {
			auto 点坐标 = (f_widget_get鼠标局部位置(globalLoc, 节点视口) - 节点视口->m_扩展视口属性->m_中心) / 节点视口->m_扩展视口属性->m_比例;
			
			//节点视口->m_剪刀线绘制.f_set下一个顶点(点坐标, S_主题颜色::uic_节点剪刀线);

			f_projectFile_设置文件保存状态(false);
		}
	}
	//std::cout << g节点视口鼠标局部位置.x << " = " << g节点视口鼠标局部位置.y << std::endl;
	return E_事件是否传递::e_事件传递_终止;
}








static E_事件是否传递 on_节点视口_按键按下(C_Widget* self, const S_键盘& ks) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);
	

	if (f_KS(ks, S_InputKey::e_KEY_左ALT) == DEF_按键状态_按下) {
		g剪刀线状态_状态 = DEF_剪刀线状态_准备;
		S_框架::g_开启节点移动 = false;
	}
	else if (f_KS(ks, S_InputKey::e_KEY_左ALT) == DEF_按键状态_放开) {
		g剪刀线状态_状态 = DEF_剪刀线状态_结束;
	}

	if (f_KS(ks, S_InputKey::e_KEY_TAB) == DEF_按键状态_放开) {
		
		//================= 同步后端数据 ==================
		f_设置当前操作节点视口(节点视口);
		f_NodeView_设置节点面板局部添加位置(节点视口, f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self), false);

		C_Widget* 菜单;
		switch (节点视口->m_节点树后端->m_树类型) {
			case E_节点树类型::e_type_包节点树:
			case E_节点树类型::e_type_物体节点树: 菜单 = C_节点视口::g物体节点创建菜单; break;

			case E_节点树类型::e_type_几何节点树: 菜单 = C_节点视口::g多边形节点创建菜单; break;
			case E_节点树类型::e_type_函数节点树: 菜单 = C_节点视口::g函数节点创建菜单; break;
			case E_节点树类型::e_type_材质节点树: 菜单 = C_节点视口::g材质节点创建菜单; break;
			case E_节点树类型::e_type_渲染节点树: 菜单 = C_节点视口::g渲染节点创建菜单; break;
			case E_节点树类型::e_type_着色节点树: 菜单 = C_节点视口::g着色器节点创建菜单; break;
			case E_节点树类型::e_type_并行节点树: 菜单 = C_节点视口::g并行节点创建菜单; break;
			case E_节点树类型::e_type_几何着色节点树: 菜单 = C_节点视口::g几何着色节点创建菜单; break;
			default:
				菜单 = nullptr;
		}

		f_ui_打开弹出窗口(菜单, C_Widget::g_鼠标.gPos);

	} 
	//else if (f_KS(ks, S_InputKey::e_KEY_N) == DEF_按键状态_放开) {
	//	节点视口->m_属性栏;
	//} 
	//else if (f_KS(ks, S_InputKey::e_KEY_X) == DEF_按键状态_放开) {
	//	f_ui_打开弹出窗口(节点视口->m_节点删除菜单, C_Widget::g_鼠标.gPos);
	//}
	
	if (f_KS(ks, S_InputKey::e_KEY_左ALT) == DEF_按键状态_放开) {
		g剪刀线状态_状态 = DEF_剪刀线状态_结束;
		S_框架::g_开启节点移动 = true;
	}

	if (f_KS(ks, S_InputKey::e_KEY_左CTRL) == DEF_按键状态_按下) {
		g剪刀线状态_状态 = DEF_剪刀线状态_准备 | DEF_剪刀线状态_添加;
		S_框架::g_开启节点移动 = false;
	}else if (f_KS(ks, S_InputKey::e_KEY_左CTRL) == DEF_按键状态_放开) {
		g剪刀线状态_状态 = DEF_剪刀线状态_结束;
		S_框架::g_开启节点移动 = true;
	}
	
	return E_事件是否传递::e_事件传递_继续;
}










/********************************************************************************
* 
* 
*							节点面板鼠标键盘事件
*
* 
*********************************************************************************/

static E_事件是否传递 f_NodeView_节点面板鼠标单击(C_Widget* self, const E_鼠标按钮& 按钮) {
	C_节点面板* 节点面板 = static_cast<C_节点面板*>(self->m_父部件);
	C_节点视口* 节点视口 = static_cast<C_节点视口*>(节点面板->m_节点当前所在视口);
	
	if (!S_框架::g_开启节点移动) {
		return E_事件是否传递::e_事件传递_终止;
	}
	else {
		C_节点面板::m_移动面板 = false;
	}

	
	C_节点操作_移动节点::g当前移动操作 = nullptr;
	if(g移动选中节点) {
		g当前移动面板操作 = nullptr;
		return E_事件是否传递::e_事件传递_终止;
	}

	std::vector<C_节点面板*> 设置的面板{ 节点面板 };
	if (节点视口->m_焦点节点面板.find(节点面板) == 节点视口->m_焦点节点面板.end()) {
		f_NodeView_添加激活节点(设置的面板, true);
		f_projectFile_设置文件保存状态(false);
		return E_事件是否传递::e_事件传递_终止;
	}

	if (节点视口->m_焦点节点面板.size() > 1) {
		f_NodeView_添加激活节点(设置的面板, true);
		f_projectFile_设置文件保存状态(false);
	}

	return E_事件是否传递::e_事件传递_终止;
}



static E_事件是否传递 on_节点面板鼠标进入(C_Widget* self, const S_鼠标& 轮值) {
	C_节点面板* plane = (C_节点面板*)self;
	C_节点视口* view = (C_节点视口*)(self->m_父部件);
	
	switch (plane->m_NodeData->m_Type) {
		case E_节点类型::e_节点Type_框: {
			//view->m_节点面板背景绘制.f_setColor(plane->m_背景GPU内存偏移 + 1, plane->m_颜色C);
			break;
		}
		default: {
			if (plane->m_NodeData->m_编译成功) {
				//view->m_节点面板背景绘制.f_setColor(plane->m_背景GPU内存偏移 + 2, plane->m_颜色C);
			}
			break;
		}
	}
	
	return E_事件是否传递::e_事件传递_继续;
}

static E_事件是否传递 on_节点面板鼠标退出(C_Widget* self, const S_鼠标& 轮值) {
	C_节点面板* plane = (C_节点面板*)self;
	C_节点视口* view = (C_节点视口*)(self->m_父部件);
	
	switch (plane->m_NodeData->m_Type) {
		case E_节点类型::e_节点Type_框: {
		
			break;
		}
		default: {
			if (plane->f_是否有焦点()) {
				//view->m_节点面板背景绘制.f_setColor(plane->m_背景GPU内存偏移 + 2, S_UI主题::uic_节点面板高亮);
			}
			else {
				//view->m_节点面板背景绘制.f_setColor(plane->m_背景GPU内存偏移 + 2, plane->m_颜色A);
			}
			break;
		}
			
	}
	return E_事件是否传递::e_事件传递_继续;
}







static vec2 g选框起点坐标;
static vec2 g选框终点坐标;

static E_事件是否传递 on_节点视口鼠标准备拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);
	
	vec2 mousePos = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
	g选框起点坐标 = (mousePos - 节点视口->m_扩展视口属性->m_中心) / 节点视口->m_扩展视口属性->m_比例;
	
	return E_事件是否传递::e_事件传递_继续;
}

static E_事件是否传递 on_节点视口准备拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);

	if (g剪刀线状态_状态) {

	}
	else {
		vec2 mousePos = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
		g选框终点坐标 = (mousePos - 节点视口->m_扩展视口属性->m_中心) / 节点视口->m_扩展视口属性->m_比例;

		data.m_Type = E_自定义拖放数据类型::e_节点视口框选;
	}
	
	return E_事件是否传递::e_事件传递_终止;
}

static bool on_节点视口鼠标结束拖动(C_Widget* self, C_Widget* 拾取的部件, S_拖放数据& data) {
	//if(data.m_Type != E_框架_拖放数据类型::e_节点视口框选) return E_事件是否传递::e_事件传递_继续;
	if (g剪刀线状态_状态) return E_事件是否传递::e_事件传递_继续;

	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);

	auto 鼠标局部位置 = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
	

	switch (data.m_Type) {
		case E_自定义拖放数据类型::e_节点面板移动: {
			break;
		}
		case E_自定义拖放数据类型::e_节点视口框选: {
			
			auto e = 节点视口->f_get更新组件();
			auto left = vec_Min(g选框起点坐标, g选框终点坐标);
			auto rigth = vec_abs(g选框终点坐标 - g选框起点坐标) + left;

			std::vector<C_节点面板*> nodes;
			nodes.reserve(100);
			for (uint32 i = 0; i < e.m_Num; ++i) {
				C_节点面板* panel = (C_节点面板*)e.m_W[i];

				vec2 pos = (panel->f_getOriginalPos() + panel->m_内部面板->f_getOriginalPos());
				vec2 r = panel->m_内部面板->f_getOriginalSize() + pos;

				if (pos.x >= left.x && pos.y >= left.y) {
					if (r.x < rigth.x && r.y < rigth.y) {
						nodes.push_back((C_节点面板*)e.m_W[i]);
					}
				}
			}

			if (nodes.size()) {
				f_NodeView_添加激活节点(nodes, true);
			}
			break;
		}
		case E_自定义拖放数据类型::e_拖放_节点子集按钮: {
			switch (节点视口->m_节点树后端->m_树类型) {
				case E_节点树类型::e_type_渲染节点树: {
					S_插座拖放数据* 节点拖放数据 = (S_插座拖放数据*)data.m_Next;

					auto* node = 节点拖放数据->发起的节点->m_NodeData;

					//=================== 创建物体引用节点 ======================
					switch (node->m_Type) {
						case E_节点类型::e_节点Type_摄像机: 
						case E_节点类型::e_节点Type_灯光: 
						case E_节点类型::e_节点Type_自定义: 
						case E_节点类型::e_节点Type_包:
						case E_节点类型::e_节点Type_变量:
						case E_节点类型::e_节点Type_计算:
						case E_节点类型::e_节点Type_多边形:
						case E_节点类型::e_节点Type_网格:
						case E_节点类型::e_节点Type_物体: {
							//auto* socket1 = node->f_get输出插座(1);
							
							C_节点面板* 新面板 = 节点视口->mf_以名称构建节点(DEF_引用物体节点);
							//f_NodeView_设置节点面板局部添加位置(鼠标局部位置);
							S_框架::g节点创建初始位置 = 鼠标局部位置 - f_NodePanel_get中心偏移(新面板);
							f_NodeView_添加节点面板到视口(节点视口, 新面板, true);
							f_NodePanel_鼠标设置节点坐标(新面板, S_框架::g节点创建初始位置);

							新面板->m_NodeData->mf_节点联动创建(新面板->m_NodeData, { node });
							//C_引用物体节点* a = 新面板->m_NodeData;
							
							break;
						}
						default:
							break;
					}
					if (node->m_TypeName == DEF_取物体节点) {

					}
					else if (node->m_TypeName == DEF_引用物体节点) {

					}
			
					break;
				}
				default:
					break;
			}

			break;
		}
		case E_自定义拖放数据类型::e_拖放_库节点模块项加载: {
			S_库节点加载* 库节点加载 = (S_库节点加载*)data.m_Next;
			std::cout<<库节点加载->m_Path<<std::endl;
		
			FILE* f = fopen(库节点加载->m_Path.c_str(), "rb");
			if (f) {
				std::vector<C_节点基类*> Nodes;
				f_node后端_设置节点树读取新建状态();
				f_node后端_加载库节点(*S_节点框架::g_3D视口环境, Nodes, f);
				fclose(f);

				f_NodeView_设置节点面板局部添加位置(节点视口, 鼠标局部位置, false);
				f_NodeView_添加节点到视口(节点视口, Nodes, true);

				for (auto& node : Nodes) {
					node->f_更新上层节点(true);
				}
			}
		
			break;
		}
		//case E_自定义拖放数据类型::e_拖放_库_插件加载: {
		//	data.m_Next;
		//	f_node后端_创建扩展节点(*S_框架::g_3D视口环境, );
		//	break;
		//}
		default:
			break;
	}
	
	f_projectFile_设置文件保存状态(false);
	f_NodeCtx_get默认场景()->m_光追后端->f_重置渲染帧记录();

	return true;
}



static void on_节点面板纵向变换修改(C_节点视口* view, C_节点面板* 节点面板) {
	mat3X2 mat;

	vec2 pos = 节点面板->f_getOriginalPos();
	vec2 内部面板位置 = pos + 节点面板->m_内部面板->f_getOriginalPos();
	vec2 内部面板大小 = 节点面板->m_内部面板->f_getOriginalSize();
	E_节点类型 节点类型 = 节点面板->m_NodeData->m_Type;

	float32 缩放 = 节点面板->m_节点当前所在视口->m_扩展视口属性->m_比例.x;

	uint32 每个面板元素数量 = g每个面板元素数量;
	{
		switch (节点类型) {
		case E_节点类型::e_节点Type_框: {
			mat.row0 = 内部面板位置;
			mat.row1 = 内部面板大小;
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };

			S_RGBA8UI 高亮;
			if (节点面板->f_是否有焦点()) {
				高亮 = S_RGBA8UI{ 60, 100, 100, 120 };
			}
			else {
				高亮 = S_RGBA8UI{ 60, 60, 60, 60 };
			}
			
			mat.row0 = 内部面板位置 + 2;
			mat.row1 = 内部面板大小 - 4;
			S_LineProp lp = { 0, f_PackData4X8(S_主题颜色::uic_节点框 + 高亮), f_PackData4X8(S_主题颜色::uic_节点框 + 高亮) };
			每个面板元素数量 = 2;
			break;
		}
		default:
			S_RGBA8UI 焦点颜色 = { 10,10,10,200 };
			if (节点面板->m_NodeData->m_冻结数据) {
				焦点颜色 = { 30,30,30,128 };
			}
			mat.row0 = 内部面板位置 + 4;
			mat.row1 = 内部面板大小;
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
			
			焦点颜色 = S_主题颜色::uic_节点面板焦点 + S_RGBA8UI{ 50,80,200,0 };
			if (节点面板->m_NodeData->m_冻结数据 == false) {
				焦点颜色 -= S_RGBA8UI{ 30, 10, 0, 0 };
			}
			mat.row0 = 内部面板位置 - 2;
			mat.row1 = 内部面板大小 + 4;
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
			


			焦点颜色 = 节点面板->m_NodeData->m_颜色;
			if (!节点面板->m_NodeData->m_编译成功) {
				焦点颜色 = S_RGBA8UI{ 255, 10, 0, 255 };
			}

			S_RGBA8UI 按钮颜色 = 节点面板->m_拖拽到其他视口按钮->m_颜色A;
			if (节点面板->m_NodeData->m_冻结数据) {
				焦点颜色 = S_RGBA8UI{ 50, 190, 240, 255 };
				按钮颜色 = S_RGBA8UI{ 20, 140, 200, 255 };
			}
			if (节点面板->m_NodeData->m_数据是否以缓存) {
				焦点颜色 = S_RGBA8UI{ 210, 160, 100, 255 };
				按钮颜色 = S_RGBA8UI{ 190, 130, 80, 255 };
			}
			
			mat.row0 = 内部面板位置;
			mat.row1 = 内部面板大小;
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
		

			mat.row0 = pos + 节点面板->m_拖拽到其他视口按钮->f_getOriginalPos();
			mat.row1 = 节点面板->m_拖拽到其他视口按钮->f_getOriginalSize();
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
			
		}
	}
	
	vec2 中心局部坐标 = f_NodePanel_get中心偏移(节点面板);
	mat.row0 = pos + 中心局部坐标;
	mat.row1.x = mat.row1.y;
	mat.row2 = {};
	//view->m_节点面板图标绘制.f_set(节点面板->m_ICO, 节点面板->m_实例ID, mat, { 0,0,0,0 });
	
	if(每个面板元素数量 <= 2) return;

	vec2 连接点大小 = g纵向排列插座大小;
	uint32 线GPU偏移 = 节点面板->m_链接线GPU内存偏移;

	uint32 线颜色 = f_PackData4X8(S_主题颜色::uic_节点链接线);
	uint32 线高亮颜色A = f_PackData4X8(S_UI主题::uic_深色 + S_UI主题::uic_面板边);
	uint32 线高亮颜色B = f_PackData4X8(S_UI主题::uic_深色 + S_RGBA8UI{ 200, 100, 20, 255 });

	S_LineProp 线属性 = { 1.0, 线颜色, 线颜色 };

	auto& inSocket = 节点面板->f_getInSocket();
	auto& outSocket = 节点面板->f_getOutSocket();

	uint32 inNum = 节点面板->m_NodeData->f_get输入插座数量();
	uint32 outNum = 节点面板->m_NodeData->f_get输出插座数量();
	//uint32 所有插座数量 = inNum + outNum;

	for (uint32 i = 0; i < inNum; ++i) {
		vec2 插座大小 = inSocket[i]->f_getOriginalSize();

		mat.row0 = pos + inSocket[i]->f_getOriginalPos();
		auto 图元 = S_Widget预设凸包图元::m_左圆角矩形;

		C_插座基类* 插座后端 = inSocket[i]->m_SocketData;
		float32 圆角半径 = 2;

		if (i) {
			插座大小.x -= 3;
			vec2 插座偏移 = 插座大小;
			
			switch (节点类型) {
				case E_节点类型::e_节点Type_框:
					break;
				case E_节点类型::e_节点Type_Reroute:
					图元 = S_Widget预设凸包图元::m_圆形;
					
					插座大小.x = DEF_Min(插座大小.x, 插座大小.y);
					插座大小.y = 插座大小.x;

					插座偏移.x = (插座偏移.x - 插座大小.x) * 0.5;
					插座偏移.y -= 插座大小.y;
					break;

				default: {
					
					if (插座后端->f_is单链接()) {
						插座大小.x = DEF_Min(插座大小.x, g纵向排列插座大小.x);
					}
					else {
						插座大小.x = DEF_Max(插座大小.x, g纵向排列插座大小.x);
					}

					插座大小.y = DEF_Min(插座大小.y, g纵向排列插座大小.y);

					插座偏移.x = (插座偏移.x - 插座大小.x) * 0.5;
					插座偏移.y -= 插座大小.y;

					图元 = S_Widget预设凸包图元::m_圆角矩形;
					圆角半径 = 3;

					
				}
			}

			mat.row0.x += 插座偏移.x + 1.5f;
			mat.row0.y += 插座偏移.y;
		}
		else {
			//插座大小
		}
		mat.row1 = 插座大小;
		mat.row2 = { 圆角半径, 0 };

		auto name = 插座后端->f_getTypeName();
		
		auto& linkLines = 插座后端->f_getLinkLines();
		uint16 k = 1;
		uint16 socketNum = linkLines.size();
		for (auto link : linkLines) {
			S_Line 线坐标 = 节点面板->f_计算横向排列插座连接线坐标(link, socketNum, k, i);

			S_CurvePoint2 point;
			point.L_point = 线坐标.start;
			point.R_point = 线坐标.end;
			point.value = { 内部面板大小.x * 0.4f, 内部面板大小.x*0.4f };
			

			if (i) {
				//图元 = S_Widget预设图元::m_竖向圆角连接线;
				point.Select_Type = { 0, DEF_LINKLINE_TYPE_CircleTangent_V };
			}
			else {
				point.Select_Type = { 0, DEF_LINKLINE_TYPE_C };
			}

			
			if (节点面板->m_NodeData->m_是否有焦点) {
				point.ID_Color = { 线高亮颜色B, 线高亮颜色B };
			}
			else if (link->m_插座out->f_getNode()->m_是否有焦点) {
				point.ID_Color = { 线高亮颜色B, 线高亮颜色B };
			}
			else {
				point.ID_Color = { 线颜色, 线颜色 };
			}

			view->m_节点面板连接线绘制.f_set(线GPU偏移, point);
			++线GPU偏移;
			++k;
		}
	}

	for (uint32 i = 0; i < outNum; ++i) {
		vec2 插座大小 = outSocket[i]->f_getOriginalSize();
		mat.row0 = pos + outSocket[i]->f_getOriginalPos();
		auto 图元 = S_Widget预设凸包图元::m_左圆角矩形;

		float32 圆角半径 = 3;
		if (i) {
			vec2 插座偏移 = 插座大小;
			插座大小 = vec_Min(连接点大小, 插座大小);

			switch (节点类型) {
				case E_节点类型::e_节点Type_框:
					break;
				case E_节点类型::e_节点Type_Reroute:
					图元 = S_Widget预设凸包图元::m_圆形;
					
					插座大小.x = DEF_Min(插座大小.x, 插座大小.y);
					插座大小.y = 插座大小.x;

					插座偏移.x = (插座偏移.x - 插座大小.x) * 0.5;
					插座偏移.y += 插座偏移.x;

					mat.row0.x += 插座偏移.x;
					break;

				default: {
					插座偏移.x = (插座偏移.x - 插座大小.x) * 0.5;
					mat.row0.x += 插座偏移.x;
					圆角半径 = 2;
					图元 = S_Widget预设凸包图元::m_圆角矩形;

				}
			}
		}

		mat.row1 = 插座大小;
		mat.row2 = { 圆角半径, 0 };

	}

}

static void on_节点面板横向变换修改(C_节点视口* view, C_节点面板* 节点面板) {
	vec2 坐标 = 节点面板->m_NodeData->m_坐标;
	vec2 比例 = 节点面板->m_节点当前所在视口->m_扩展视口属性->m_比例;
	vec2 变换前偏移 = 节点面板->m_父部件->m_扩展视口属性->m_中心 / 比例;
	vec2 内部面板大小 = 节点面板->m_内部面板->f_getOriginalSize();

	E_节点类型 节点类型 = 节点面板->m_NodeData->m_Type;
	uint32 每个面板元素数量 = g每个面板元素数量;
	float32 圆角 = S_UI主题::ui_面板圆角半径;

	vec2 面板位置 = 坐标 + 节点面板->m_内部面板->f_getOriginalPos();
	mat3X2 mat;
	{
		
		vec2 面板大小 = 节点面板->m_内部面板->f_getOriginalSize();
		float32 阴影 = 4;

		switch (节点面板->m_NodeData->m_Type) {
		case E_节点类型::e_节点Type_框: {
			mat.row0 = 面板位置;
			mat.row1 = 内部面板大小;
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };

			S_RGBA8UI 高亮;
			if (节点面板->f_是否有焦点()) {
				高亮 = S_RGBA8UI{ 60, 100, 180, 120 };
			}
			else {
				高亮 = S_RGBA8UI{ 60, 60, 60, 60 };
			}
			
			mat.row0 = 面板位置 + 2;
			mat.row1 = 内部面板大小 - 4;
			S_LineProp lp = {0, f_PackData4X8(S_主题颜色::uic_节点框 + 高亮), f_PackData4X8(S_主题颜色::uic_节点框 + 高亮) };
			每个面板元素数量 = 2;
			break;
		}
		case E_节点类型::e_节点Type_网格属性:
		case E_节点类型::e_节点Type_类成员:
		case E_节点类型::e_节点Type_全局: {
			坐标 -= 变换前偏移;
			面板位置 -= 变换前偏移;
			内部面板大小 /= 比例;
			圆角 /= 比例.x;
			阴影 /= 比例.x;
		}
		default:

			S_RGBA8UI 焦点颜色 = { 10,10,10,200 };
			if (节点面板->m_NodeData->m_冻结数据) {
				焦点颜色 = { 30,30,30,128 };
			}
			mat.row0 = 面板位置 + 阴影;
			mat.row1 = 内部面板大小;
			mat.row2 = { 圆角, 0 };
			
			焦点颜色 = S_主题颜色::uic_节点面板焦点 + S_RGBA8UI{ 50,80,200,0 };
			if (节点面板->m_NodeData->m_冻结数据 == false) {
				焦点颜色 -= S_RGBA8UI{ 30, 10, 0, 0 };
			}
			
			mat.row0 = 面板位置 - 阴影*0.5;
			mat.row1 = 内部面板大小 + 阴影;
			mat.row2 = { 圆角, 0 };
			

			焦点颜色 = 节点面板->m_NodeData->m_颜色;
			if (!节点面板->m_NodeData->m_编译成功) {
				焦点颜色 = S_RGBA8UI{ 255, 10, 0, 255 };
			}
			mat.row0 = 面板位置;
			mat.row1 = 内部面板大小;
			mat.row2 = { 圆角, 0 };
			

			mat.row0 = 坐标 + 节点面板->m_拖拽到其他视口按钮->f_getOriginalPos();
			mat.row1 = 节点面板->m_拖拽到其他视口按钮->f_getOriginalSize();
			mat.row2 = { 圆角, 0 };
			

			mat.row0 = 坐标 + 节点面板->m_独显按钮->f_getOriginalPos();
			mat.row1 = 节点面板->m_独显按钮->f_getOriginalSize();
			mat.row2 = { 圆角, 0 };
		}
	}

	float32 图标大小 = 35;
	vec2 外框大小 = 节点面板->f_getOriginalSize();
	mat.row0 = 坐标 + vec2{外框大小.x * 0.5f, 外框大小.y + 图标大小*0.35f};
	mat.row1.x = mat.row1.y = 图标大小;
	mat.row2 = {};
	//view->m_节点面板图标绘制.f_set(节点面板->m_ICO, 节点面板->m_实例ID, mat, { 0,0,0,0 });

	if (比例.x > 0.6) {
		auto 文字颜色 = S_UI主题::uic_文字颜色;
		auto name = 节点面板->m_NodeData->f_getWName();
		
		switch (节点类型) {
			case E_节点类型::e_节点Type_框:

				mat.row1.x = 内部面板大小.x;
				mat.row1.y = 20;
				break;

			case E_节点类型::e_节点Type_网格属性:
			case E_节点类型::e_节点Type_类成员:
			case E_节点类型::e_节点Type_全局: {
				mat.row0 = 面板位置 + (vec2{ 4, 4 }) / 比例;
				//mat.row0 = {0,0};
				mat.row1 = vec2{ 200, 20 } / 比例;

				文字颜色 = {10,10,10,255};
				break;
			}
			default: {
				mat.row0 = 坐标;
				mat.row0.x += 节点面板->f_getOriginalSize().x + 5;
				mat.row0.y += (节点面板->f_getOriginalSize().y * 0.5f);
				mat.row1.x = 200 * 比例.x;
				mat.row1.y = 1;
				break;
			}
		}
	}
	else {
		view->m_节点面板文本绘制.f_Close();
	}
	if (每个面板元素数量 <= 2) return;


	
	{
		auto& inSocket = 节点面板->f_getInSocket();
		auto& outSocket = 节点面板->f_getOutSocket();

		uint32 inNum = inSocket.size();
		uint32 outNum = outSocket.size();

		S_LineProp 线属性;
		uint32 线颜色 = f_PackData4X8(S_主题颜色::uic_节点链接线);
		uint32 线高亮颜色A = f_PackData4X8(S_主题颜色::uic_节点链接线 + S_UI主题::uic_面板边);
		uint32 线高亮颜色B = f_PackData4X8(S_主题颜色::uic_节点链接线 + S_RGBA8UI{ 200, 100, 20, 255 });

		
		线属性 = { 1.0, 线颜色, 线颜色 };
		float32 插座空白宽度 = g插座空白宽度 / 比例.x;

		
		vec2 连接点大小 = g纵向排列插座大小;
		uint32 线GPU偏移 = 节点面板->m_链接线GPU内存偏移;

		for (uint32 i = 0; i < inNum; ++i) {
			
			vec2 插座大小 = inSocket[i]->f_getOriginalSize();
			auto 图元 = S_Widget预设凸包图元::m_上圆角矩形;

			float32 圆角半径 = 2;
			
			vec2 插座偏移 = 插座大小;

			switch (节点类型) {
				case E_节点类型::e_节点Type_网格属性:
				case E_节点类型::e_节点Type_类成员:
				case E_节点类型::e_节点Type_全局: {
					mat.row0 = 坐标 + inSocket[i]->f_getOriginalPos() / 比例;
					插座大小 /= 比例;
					插座大小 -= 插座空白宽度;
					圆角半径 = 插座空白宽度;
					
					break;
				}
				case E_节点类型::e_节点Type_Reroute: {
					图元 = S_Widget预设凸包图元::m_圆形;

					插座偏移.x = DEF_Min(插座偏移.x, 插座偏移.y);
					插座偏移.y = 插座偏移.x;

					mat.row0 = 坐标 + inSocket[i]->f_getOriginalPos();
					mat.row0.x += (插座大小.x - 插座偏移.x) * 0.5;
					mat.row0.y += (插座大小.y - 插座偏移.y) * 0.5;

					插座大小.x = DEF_Min(插座大小.x, 插座大小.y);
					插座大小.y = 插座大小.x;
					break;
				}
				default:{
					if (inSocket[i]->m_SocketData->f_is单链接()) {
						插座大小.x = DEF_Min(插座大小.x, g纵向排列插座大小.x);
					}
					else {
						插座大小.x = DEF_Min(插座大小.x, g纵向排列插座大小.x);
					}
					插座大小 -= 2;
					圆角半径 = 3;

					mat.row0 = 坐标 + inSocket[i]->f_getOriginalPos();
					mat.row0.x += 2;
					图元 = S_Widget预设凸包图元::m_左圆角矩形;
				}
			}

			mat.row1 = 插座大小;
			mat.row2 = { 圆角半径, 0 };
			

			if (i && 比例.x >= 1) {
				auto name = inSocket[i]->m_SocketData->f_getTypeName() + u":" + inSocket[i]->m_SocketData->m_identifier;
				//view->m_节点面板文本绘制.f_绘制文本({ mat.row0 , mat.row1 }, S_主题颜色::uic_节点插座文字, 文字GPU偏移, name, E_对齐方式::e_左对齐);
			}




			auto& linkLines = inSocket[i]->m_SocketData->f_getLinkLines();
			uint16 k = 1;
			uint16 socketNum = linkLines.size();
			for (auto link : linkLines) {
				S_Line 线坐标 = 节点面板->f_计算纵向排列插座连接线坐标(link, socketNum, k, i, 变换前偏移, 比例.x);

				vec2 大小 = 线坐标.end - 线坐标.start;
				mat3X2 mat;
				mat.row0 = 线坐标.start;
				mat.row1 = 大小;
				mat.row2 = { DEF_Min(fabs(大小.x), fabs(大小.y)) * 0.5f, 0 };

				S_CurvePoint2 point;
				point.L_point = 线坐标.start;
				point.R_point = 线坐标.end;
				point.value = { g链接线圆角最大半径, g链接线圆角最大半径 };
				

				

				if (节点面板->m_NodeData->m_是否有焦点) {
					point.ID_Color = { 线高亮颜色B, 线高亮颜色B };
				}
				else if (link->m_插座out->f_getNode()->m_是否有焦点) {
					point.ID_Color = { 线高亮颜色A, 线高亮颜色B };
				}
				else {
					point.ID_Color = { 线颜色, 线颜色 };
				}

				if (i) {
					//图元 = S_Widget预设图元::m_横向圆角连接线;
					point.Select_Type = { 0, DEF_LINKLINE_TYPE_CircleTangent_H };
				}
				else {
					point.Select_Type = { 0, DEF_LINKLINE_TYPE_CircleTangent_H };

					uint32 执行循序线颜色 = f_PackData4X8(S_RGBA8UI{ 20, 80, 100, 255 });
					point.ID_Color = { 执行循序线颜色, 执行循序线颜色 };
				}

				view->m_节点面板连接线绘制.f_set(线GPU偏移, point);
				++线GPU偏移;
				++k;
			}
		}

		for (uint32 i = 0; i < outNum; ++i) {
			vec2 插座大小 = outSocket[i]->f_getOriginalSize();
			
			
			auto 图元 = S_Widget预设凸包图元::m_下圆角矩形;
			
			float32 圆角半径 = 3;
			vec2 插座偏移 = 插座大小;

			switch (节点类型) {
				case E_节点类型::e_节点Type_网格属性:
				case E_节点类型::e_节点Type_类成员:
				case E_节点类型::e_节点Type_全局: {
					mat.row0 = 坐标 + outSocket[i]->f_getOriginalPos() / 比例;
					插座大小 /= 比例;
					圆角半径 = 插座空白宽度;
					插座大小 -= 插座空白宽度;

					图元 = S_Widget预设凸包图元::m_右圆角矩形;
					break;
				}
				case E_节点类型::e_节点Type_Reroute: {
					插座偏移.x = DEF_Min(插座偏移.x, 插座偏移.y);
					插座偏移.y = 插座偏移.x;

					mat.row0 = 坐标 + outSocket[i]->f_getOriginalPos();
					mat.row0.x += (插座大小.x - 插座偏移.x) * 0.5;
					mat.row0.y += (插座大小.y - 插座偏移.y) * 0.5;

					插座大小.x = DEF_Min(插座大小.x, 插座大小.y);
					插座大小.y = 插座大小.x;

					图元 = S_Widget预设凸包图元::m_圆形;
					break;
				}
					
				default: {
					mat.row0 = 坐标 + outSocket[i]->f_getOriginalPos();
					mat.row0.y += 插座空白宽度;
					
					插座大小 -= 2;
					圆角半径 = 3;

					图元 = S_Widget预设凸包图元::m_右圆角矩形;
				}
			}
			mat.row1 = 插座大小;
			mat.row2 = { 圆角半径, 0 };
			
			if (i && 比例.x >= 0.5) {
				auto name = outSocket[i]->m_SocketData->f_getTypeName() + u":" + outSocket[i]->m_SocketData->m_identifier;
				
				mat.row0.x -= 内部面板大小.x;
				mat.row1.x = 内部面板大小.x;
				mat.row1.y = 15 / 比例.x;
				//view->m_节点面板文本绘制.f_绘制文本({ mat.row0 , mat.row1 }, S_主题颜色::uic_节点插座文字, 文字GPU偏移, name, E_对齐方式::e_右对齐, 比例.x);
			}
			else {
				//view->m_节点面板文本绘制.f_绘制文本({ mat.row0 , mat.row1 }, S_主题颜色::uic_节点插座文字, 文字GPU偏移, u"", E_对齐方式::e_右对齐);
			}
		}
	}


}

static void on_节点视口布局(C_Widget* self, C_Widget** w, uint32 num) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);

	vec2 比例 = 节点视口->m_扩展视口属性->m_比例;
	vec2 变换前偏移 = 节点视口->m_扩展视口属性->m_中心 / 比例;


	float32 y = 0;
	if (节点视口->m_节点树后端->m_全局变量) {
		节点视口->m_节点树后端->m_全局变量->m_坐标.x = (节点视口->m_属性栏->m_Size.x - 10) / 比例.x;
		节点视口->m_节点树后端->m_全局变量->m_坐标.y = y / 比例.x;

		if (节点视口->m_节点树后端->m_全局变量->m_包含在多个视口的UI部件.size()) {
			C_节点面板* panel = static_cast<C_节点面板*>(*节点视口->m_节点树后端->m_全局变量->m_包含在多个视口的UI部件.begin());
			f_NodePanel_更新布局(panel);

			y += panel->f_getOriginalSize().y;
		}
	}

	if (节点视口->m_节点树后端->m_成员变量) {
		节点视口->m_节点树后端->m_成员变量->m_坐标.x = (节点视口->m_属性栏->m_Size.x - 10) / 比例.x;
		节点视口->m_节点树后端->m_成员变量->m_坐标.y = y / 比例.x;

		C_节点面板* panel = static_cast<C_节点面板*>(*节点视口->m_节点树后端->m_成员变量->m_包含在多个视口的UI部件.begin());
		f_NodePanel_更新布局(panel);

		y += panel->f_getOriginalSize().y;
	}

	if (节点视口->m_节点树后端->m_网格属性变量) {
		节点视口->m_节点树后端->m_网格属性变量->m_坐标.x = (节点视口->m_属性栏->m_Size.x - 10) / 比例.x;
		节点视口->m_节点树后端->m_网格属性变量->m_坐标.y = y / 比例.x;

		if (节点视口->m_节点树后端->m_网格属性变量->m_包含在多个视口的UI部件.size()) {
			C_节点面板* panel = static_cast<C_节点面板*>(*节点视口->m_节点树后端->m_网格属性变量->m_包含在多个视口的UI部件.begin());
			f_NodePanel_更新布局(panel);
		}
	}

	for (uint32 i = 0; i < num; ++i) {
		C_节点面板* node = static_cast<C_节点面板*>(w[i]);

		switch (node->m_NodeData->m_Type) {
			case E_节点类型::e_节点Type_框: {
				break;
			}
			default: {
				f_NodePanel_更新布局(node);
			}
		}
	}

	for (uint32 i = 0; i < num; ++i) {
		C_节点面板* node = static_cast<C_节点面板*>(w[i]);

		switch (node->m_NodeData->m_Type) {
			case E_节点类型::e_节点Type_框: {
				if (!node->m_NodeData->m_框节点) {
					f_NodePanel_更新框(node);
				}
				break;
			}
		}
	}

	//f_节点缩放(节点视口);
	//节点视口->f_Scale();
}


static void on_节点视口绘制(C_Widget* self, S_2D画布* 画布) {
	C_节点视口& box = *static_cast<C_节点视口*>(self);
	
	box.m_背景	= f_vg_genConvex((*self), *画布, E_填充模式::e_填充面, E_图层混合模式::e_Normal);
	box.m_字符 = f_vg_genString((*self), *画布, E_填充模式::e_填充面, E_图层混合模式::e_Normal, u"雅黑");
	
}

static void on_节点视口变换(C_Widget* self) {
	C_节点视口& box = *static_cast<C_节点视口*>(self);
	float32 dpi = box.m_UIctx->m_DPI;

	std::vector<S_2DConvexRect>	区域(1);
	std::vector<S_GPU内存块*>	形状(1);
	std::vector<S_2D颜色包>		颜色(1);
	std::vector<vec2>			位置(1);

	
	auto& nodes = box.m_节点树后端->m_Nodes;
	auto e = box.f_get更新组件();
	
	uint32 节点数量 = nodes.size();



	区域[0] = {{}, vec2{256,256} * box.m_Scale, {4,4}, {1,0}};
	形状[0] = &self->m_UIctx->m_Ctx.m_几何图形->m_平面圆角矩形;
	颜色[0] = S_2D颜色包({50,180,200,128},128,0,0);
	位置[0] = vec2{800,0} * box.m_Scale * dpi;
	
	f_vg_drawConvex(*box.m_背景, 1, 形状.data(), 区域.data());
	f_vg_tranform(*box.m_背景, 1, 位置.data());
	f_vg_color(*box.m_背景, 1, 颜色.data());


	std::vector<std::u16string> 标签字符;
	std::vector<S_RGBA8UI>		标签颜色;
	std::vector<S_Rect2Df>		标签区域;

	标签字符.push_back(u"后续计划; 节点系统重构,场景缩放置鼠标位置,模型纹理显示,LUA函数绑定, 节点系统构建,LUA调试,用LUA构建自定义节点;");
	标签字符.push_back(u"0.4.41 物体创建节点系统;");
	标签字符.push_back(u"0.4.40 GPU缓存拷贝优化; 物体更新优化;");
	标签字符.push_back(u"0.4.38 UI绘制优化;");
	标签字符.push_back(u"0.4.37 UI框架优化;");
	标签字符.push_back(u"0.4.34 修正多物体旋转;");
	标签字符.push_back(u"0.4.33 锁定轴移动修正;");
	标签字符.push_back(u"0.4.32 文件新建 加载 保存; 加载信息显示;");
	标签字符.push_back(u"0.4.28 UI字体绘制修正;");
	标签字符.push_back(u"0.4.27 输入数值精确变换;");
	标签字符.push_back(u"0.4.26 变换轴锁定;");
	标签字符.push_back(u"0.4.25 LUA函数绑定支持；导入模型调整；图标绘制修正;");
	标签字符.push_back(u"0.4.24 场景视角切换; 调整窗口事件; 修正大纲区间选择;");
	标签字符.push_back(u"0.4.23 视口物体ID和光照渲染;");
	标签字符.push_back(u"0.4.22 反向框选; 反选; 窗口事件调整;");
	标签字符.push_back(u"0.4.21 记录日志;");
	标签字符.push_back(u"0.4.20 3D视口支持拖入FBX文件导入; 物体拾取修正;");
	标签字符.push_back(u"0.4.19 多边形边和点绘制; 优化物体和视口移动;");
	标签字符.push_back(u"0.4.18 删除物体");
	标签字符.push_back(u"0.4.17 复制物体");
	标签字符.push_back(u"0.4.16 恢复框选物体");
	标签字符.push_back(u"0.4.15 添加平面移动变换操作");
	标签字符.push_back(u"0.4.14 重构3D视口左边属性栏；修正物体包围盒变换");
	标签字符.push_back(u"0.4.13 终端代码块编辑");
	标签字符.push_back(u"0.4.12 修正缩放操作");
	标签字符.push_back(u"0.4.11 LUA终端代码补全");
	标签字符.push_back(u"0.4.10 新增LUA终端");
	标签字符.push_back(u"界面图层还有问题");
	标签字符.push_back(u"老节点系统作废 新节点系统还在琢磨");

	vec2 offset{};
	S_RGBA8UI color{0,200,0,255};
	for (auto& e : 标签字符) {
		color.b += 15;
		color.r = e.size()*4;
		标签颜色.push_back(color);
		标签区域.push_back({offset, vec2{800,30} * box.m_Scale * dpi });

		offset.y += 30 * box.m_Scale.x * dpi;
	}



	S_行字符串绘制属性 属性;
	属性.m_方向 = E_方向::e_横向;
	属性.m_间距 = S_UI主题::ui_文字间隔 * box.m_Scale.x * dpi;
	f_vg_drawString(*box.m_字符, 标签字符, 标签区域, 标签颜色, E_对齐方式::e_左对齐, 属性);

	return;

}

C_节点视口::C_节点视口(S_UI渲染环境& ctx, C_节点树* tree) : C_Widget(&ctx), m_节点树后端(tree) {
	m_视图大小		= { 4096, 4096 };
	f_setMinSize(m_视图大小);


	mf_绘制 = on_节点视口绘制;
	mf_布局 = on_节点视口布局;
	mf_变换 = on_节点视口变换;
	f_widget_添加绘制画布(this);

	mf_文件处理回调 = on_文件拖放_节点创建回调;
	mf_键盘按键敲击	= on_节点视口_按键按下;


	mf_以名称构建节点 = nullptr;
	mf_以节点构建节点 = nullptr;
	mf_节点重构回调 = nullptr;

	m_WName = u"节点视口";
	m_TypeName = "C_Widget 节点视口";
	
	

	m_节点创建菜单 = 0;
	m_节点删除菜单 = 0;

	g节点视口[tree].insert(this);

}

C_节点视口::~C_节点视口() {
}


void C_节点视口::f_nodePanel_移除(std::vector<C_节点面板*>& nodes) {
	std::vector<C_Widget*> 节点面板组件组;
	for (auto& k : nodes) {
		f_node_节点树删除节点(m_节点树后端, k->m_NodeData);
		节点面板组件组.push_back(k);
	}
	f_widget_remove子部件(this, 节点面板组件组, false);
}

vec2 C_节点视口::f_get边界() {
	return m_视图大小;
}

void C_节点视口::f_update节点面板(C_Widget* w) {
	w->f_setPos(w->f_getOriginalPos() * m_扩展视口属性->m_比例 + m_扩展视口属性->m_中心);
	w->f_setSize(w->f_getOriginalSize() * m_扩展视口属性->m_比例);
	w->m_Scale = m_扩展视口属性->m_比例;
}

void C_节点视口::f_Scale() {
	



}

void C_节点视口::f_nodeTree_绑定(C_节点树* tree, C_节点面板* root_plane) {
	f_清除所有节点面板();

	if (m_节点树后端) {
		g节点视口[m_节点树后端].erase(this);
	}

	assert(tree);

	//======================== 重建 ========================
	m_节点树后端 = tree;
	f_设置当前操作节点视口(this);
	g节点视口[tree].insert(this);
	
	
	uint32 节点数据数量 = tree->m_Nodes.size();
	
	auto iter = tree->m_Nodes.begin();
	for (uint32 i = 0; i < 节点数据数量; ++i, ++iter) {
		C_节点面板* plane = 0;
		
		plane = f_NodeView_创建节点面板(*iter, this);
		plane->m_实例ID = i;
	}

	if (tree->m_全局变量) {
		auto plane = f_NodeView_创建节点面板(tree->m_全局变量, this);
		plane->m_实例ID = 节点数据数量;
		++节点数据数量;
	}
	if (tree->m_成员变量) {
		auto plane = f_NodeView_创建节点面板(tree->m_成员变量, this);
		plane->m_实例ID = 节点数据数量;
		++节点数据数量;
	}
	if (tree->m_网格属性变量) {
		auto plane = f_NodeView_创建节点面板(tree->m_网格属性变量, this);
		plane->m_实例ID = 节点数据数量;
	}
}


void C_节点视口::f_清除所有节点面板(){
	for (auto* 节点面板 : m_焦点节点面板) {
		for (auto& node : 节点面板->m_NodeData->m_包含在多个视口的UI部件) {
			static_cast<C_节点面板*>(node)->f_设置焦点(false);
		}
	}
	m_焦点节点面板.clear();

	f_projectFile_清除焦点更新节点();



	//清除已有的节点面板
	for (auto& e : m_子组件) {
		f_节点面板从节点视口移入回收站((C_节点面板*)e);
	}
	f_widget_remove所有子部件(this, false);

	m_节点面板垃圾回收.clear();
	m_焦点节点面板.clear();
	m_子组件.clear();
	
}

C_节点面板* C_节点视口::f_从节点后端创建节点面板(C_节点基类* node, uint32 ID) {
	C_节点面板* plane = mf_节点重构回调(nullptr, node);

	f_节点面板添加到节点视口(plane, ID);
	
	return plane;
}

void C_节点视口::f_节点面板添加到节点视口(C_节点面板* plane, uint32 ID) {
	
	f_NodeView_节点回调事件初始化(plane);
	f_面板组件添加到视口中(plane);
	
	
	plane->f_setOriginalPos(plane->m_NodeData->m_坐标);
	plane->m_节点当前所在视口 = this;
	plane->m_实例ID = ID;


	f_NodePanel_更新节点内部面板(plane);
	f_widget_单个部件裁剪(plane, this->m_裁剪大小, this->m_GlobalLoc);
}

void C_节点视口::f_面板组件添加到视口中(C_节点面板* panel) {
	switch (panel->m_NodeData->m_Type) {
	case E_节点类型::e_节点Type_框:
		f_添加子组件(panel, 0);
		break;
	default:
		f_添加子组件(panel);
		break;
	}
}

void C_节点视口::f_节点面板从节点视口移入回收站(C_节点面板* self) {
	self->m_是否已弃用 = true;
	
	self->m_NodeData->m_包含在多个视口的UI部件.erase(self);
	
	self->m_节点当前所在视口 = nullptr;
	self->m_文字GPU内存偏移 = 0;
	self->m_链接线GPU内存偏移 = 0;

	
	uint32 sockeNum = self->m_NodeData->f_get输入插座数量();
	for (uint32 i = 0; i < sockeNum; ++i) {
		self->m_NodeData->f_get输入插座(i)->m_UI部件 = nullptr;
		self->m_NodeData->f_get输入插座(i)->on_UI更新 = nullptr;
	}
	sockeNum = self->m_NodeData->f_get输出插座数量();
	for (uint32 i = 0; i < sockeNum; ++i) {
		self->m_NodeData->f_get输出插座(i)->m_UI部件 = nullptr;
		self->m_NodeData->f_get输出插座(i)->on_UI更新 = nullptr;
	}


	auto* tree = self->m_NodeData->f_get子集树();
	if (tree) {
		if (g节点视口.find(tree) != g节点视口.end()) {
			for (auto& t : g节点视口[tree]) {
				if (t != this) {
					//t->f_清除所有节点面板();
					//t->f_节点面板从节点视口移入回收站();
				}
			}
		}
	}

	m_节点面板垃圾回收.push_back(self);

	f_widget_clear焦点组件();
}

C_节点面板* C_节点视口::f_回收面板组件() {
	C_节点面板* plane = m_节点面板垃圾回收.back();
	plane->m_是否已弃用 = false;
	plane->m_文字GPU内存偏移 = 0;
	plane->m_链接线GPU内存偏移 = 0;
	m_节点面板垃圾回收.pop_back();
	return plane;
}

void C_节点视口::f_连接线线尾拖动(vec2 beginLoc, vec2 endLoc, S_RGBA8UI c, uint32 ID) {
	m_连接线绘制.f_alloc(ID+1);

	uint32 线颜色 = f_PackData4X8(c);
	S_LineProp 线属性 = { 1.0, 线颜色, 线颜色 };
	//m_连接线绘制.f_set单色线(ID, mat, 线属性, S_Widget预设图元::m_斜线, 2.0);


	S_CurvePoint2 point;
	point.L_point = beginLoc;
	point.R_point = endLoc;
	point.value = {30,30};
	point.ID_Color = { 0, 线颜色 };
	point.Select_Type = {0,0};

	switch (m_节点树后端->m_布局方向) {
		case E_方向::e_横向: {
			point.Select_Type.y = DEF_LINKLINE_TYPE_CircleTangent_H;
			break;
		}
		case E_方向::e_纵向: {
			point.Select_Type.y = DEF_LINKLINE_TYPE_CircleTangent_V;
			break;
		}
	}

	m_连接线绘制.f_set(ID, point);
	return;
}

void C_节点视口::f_绘制对齐线(vec2 beginLoc, vec2 endLoc, S_RGBA8UI c, uint32 ID) {
	m_连接线绘制.f_alloc(ID + 1);

	//vec2 大小 = endLoc - beginLoc;
	//mat3X2 mat;
	//mat.row0 = beginLoc;
	//mat.row1 = 大小 + 1;
	//mat.row1 = endLoc;
	//mat.row2 = { 1, 0 };

	uint32 线颜色 = f_PackData4X8(c);
	S_LineProp 线属性 = { 1.0, 线颜色, 线颜色 };


	S_CurvePoint2 point;
	point.L_point = vec_Min(beginLoc, endLoc);
	point.R_point = vec_Max(endLoc, beginLoc);
	point.value = { 10,10 };
	point.ID_Color = { 0, 线颜色 };
	point.Select_Type = { 0,0 };

	
	switch (m_节点树后端->m_布局方向) {
	case E_方向::e_横向: {
		point.Select_Type.y = DEF_LINKLINE_TYPE_CircleTangent_H;
		break;
	}
	case E_方向::e_纵向: {
		point.Select_Type.y = DEF_LINKLINE_TYPE_CircleTangent_V;
		break;
	}
	}

	m_连接线绘制.f_set(ID, point);
	
	return;
}

void C_节点视口::f_连接线线尾拖动结束() {
	m_连接线绘制.f_Close();
}





C_节点树视口::C_节点树视口(S_UI渲染环境& ctx) : C_Widget(&ctx) {
	mf_绘制 = on_节点视口绘制;
	mf_布局 = on_节点视口布局;
	mf_变换 = on_节点视口变换;


	mf_开始拖拽 = on_节点视口鼠标准备拖动;
	mf_拖拽 = on_节点视口准备拖动;
	mf_结束拖拽 = on_节点视口鼠标结束拖动;

}

C_节点树视口::~C_节点树视口() {

}






bool on_项选择_切换节点树(C_Widget* self, const S_列表填充数据& item) {
	C_节点视口状态条* bar = static_cast<C_节点视口状态条*>(self->m_父部件->m_父部件);

	C_节点树* tree = (C_节点树*)item.m_Data;
	bar->m_View->f_nodeTree_绑定(tree);

	return true;
}









E_事件是否传递 f_NodeView_浅拷贝节点到剪切板(C_Widget* self, E_键位执行状态 状态) {
	C_节点视口* view = dynamic_cast<C_节点视口*>(self);
	if (view) {
		S_关联拷贝节点剪切板* 剪切板 = new S_关联拷贝节点剪切板(view->m_焦点节点面板);
		剪切板->f_复制();
	}
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_从剪切板粘贴拷拷贝节点(C_Widget* self, E_键位执行状态 状态) {
	C_节点视口* view = dynamic_cast<C_节点视口*>(self);
	if (view) {
		f_设置当前操作节点视口(view);

		auto pos = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
		f_NodeView_设置节点面板局部添加位置(view, pos, false);

		S_关联拷贝节点剪切板* 剪切板 = dynamic_cast<S_关联拷贝节点剪切板*>(S_剪切板::f_get());
		if (剪切板) {
			剪切板->f_粘贴();
		}
	}
	return E_事件是否传递::e_事件传递_终止;
}



E_事件是否传递 f_NodeView_深拷贝节点到剪切板(C_Widget* self, E_键位执行状态 状态) {
	C_节点视口* view = dynamic_cast<C_节点视口*>(self);
	if (view) {
		S_深拷贝节点剪切板* 剪切板 = new S_深拷贝节点剪切板(view->m_焦点节点面板);
		剪切板->f_复制();
	}
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_从剪切板粘贴深拷贝节点(C_Widget* self, E_键位执行状态 状态) {
	C_节点视口* view = dynamic_cast<C_节点视口*>(self);
	if (view) {
		f_设置当前操作节点视口(view);

		auto pos = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
		f_NodeView_设置节点面板局部添加位置(view, pos, false);

		S_深拷贝节点剪切板* 剪切板 = dynamic_cast<S_深拷贝节点剪切板*>(S_剪切板::f_get());
		if (剪切板) {
			剪切板->f_粘贴();
		}
	}
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_关联拷贝节点(C_Widget* self, E_键位执行状态 状态) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);
	
	std::vector<C_节点基类*> 要拷贝的节点;
	//要拷贝的节点.reserve(g当前操作节点视口->m_焦点节点面板.size());
	//for (auto& e : g当前操作节点视口->m_焦点节点面板) {
	//	要拷贝的节点.push_back(e->m_NodeData);
	//}

	auto new_panel = f_NodeView_拷贝节点(节点视口, 要拷贝的节点, false);
	uint32 num = 要拷贝的节点.size();

	std::vector<C_节点基类*> 新节点(num);
	for (uint32 i = 0; i < num; ++i) {
		for (auto& nodePanel : 要拷贝的节点[i]->m_包含在多个视口的UI部件) {
			new_panel[i]->m_同子集节点树关联UI部件 = ((C_节点面板*)nodePanel)->m_同子集节点树关联UI部件;
		}
		if (!new_panel[i]->m_同子集节点树关联UI部件) {
			new_panel[i]->m_同子集节点树关联UI部件 = new std::set<C_Widget*>();
		}
		(*new_panel[i]->m_同子集节点树关联UI部件).insert(new_panel[i]);

		新节点[i] = new_panel[i]->m_NodeData;
	}


	auto 初始坐标 = (f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, 节点视口));
	初始坐标 = f_NodePanel_get标准坐标(初始坐标, 节点视口);
	g当前移动面板操作 = new C_节点操作_移动节点(新节点, 初始坐标);

	f_节点缩放(节点视口);
	g移动选中节点 = true;
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_深度拷贝节点(C_Widget* self, E_键位执行状态 状态) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);

	std::vector<C_节点基类*> 要拷贝的节点;
	//要拷贝的节点.reserve(g当前操作节点视口->m_焦点节点面板.size());
	//for (auto& e : g当前操作节点视口->m_焦点节点面板) {
	//	要拷贝的节点.push_back(e->m_NodeData);
	//}

	f_NodeView_拷贝节点(节点视口, 要拷贝的节点, true);


	std::vector<C_节点基类*> nodes;
	for (auto& panel : 节点视口->m_焦点节点面板) {
		nodes.push_back(panel->m_NodeData);
	}

	auto 初始坐标 = (f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, 节点视口));
	初始坐标 = f_NodePanel_get标准坐标(初始坐标, 节点视口);
	g当前移动面板操作 = new C_节点操作_移动节点(nodes, 初始坐标);


	g移动选中节点 = true;
	return E_事件是否传递::e_事件传递_终止;
}

static void f_ui_返回上级菜单项构建(std::set<C_节点基类*>& trees, uint32 深度) {
	uint32 同级ID = 0;
	for (auto& e : trees) {
		C_节点树* t = e->f_get父节点树();
		if (g返回菜单以记录节点树.find(t) == g返回菜单以记录节点树.end()) {
			S_列表填充数据 item{};

			item.m_item.push_back(t->m_IDName);
			if (e->f_get子集树()) {
				item.m_item.push_back(e->f_get子集树()->m_IDName);
			}
			item.m_item.push_back(e->f_getWName());
			item.m_Data = t;
			
			f_ui_添加扩展弹出菜单项(C_节点视口::g节点层级切换面板, item, { 深度, 同级ID });
			g返回菜单以记录节点树.insert(t);

			f_ui_返回上级菜单项构建(t->m_节点树绑定的父节点, 深度 + 1);
		}
	}
}

static bool f_用菜单_切换节点视口绑定的节点树(C_Widget* view, const S_列表填充数据& item) {
	C_节点树* t = (C_节点树*)(item.m_Data);
	//g当前操作节点视口->f_nodeTree_绑定(t);
	//f_NodeView_绑定节点视口弹出菜单(t->m_树类型, g当前操作节点视口);

	switch (t->m_树类型) {
	case E_节点树类型::e_type_物体节点树:
	case E_节点树类型::e_type_包节点树:
	case E_节点树类型::e_type_材质节点树:
	case E_节点树类型::e_type_着色节点树:
	case E_节点树类型::e_type_几何着色节点树:
	case E_节点树类型::e_type_渲染节点树:
	case E_节点树类型::e_type_函数节点树:
	case E_节点树类型::e_type_并行节点树:
		f_global_set全局物体显示();
		//f_global_set独立物体显示(t);
		break;
	default:
		f_global_set独立物体显示(t);
		break;
	}

	return true;
}

E_事件是否传递 f_NodeView_快捷键_返回上级节点(C_Widget* self, E_键位执行状态 状态) {
	C_节点视口* view = static_cast<C_节点视口*>(self);
	f_设置当前操作节点视口(view);

	if (view->m_节点树后端->m_节点树绑定的父节点.size() > 2) {
		f_NodeView_打开返回上级节点面板(self, 状态);
	}
	else if (view->m_节点树后端->m_节点树绑定的父节点.size() >= 1) {
		auto 返回的节点 = *view->m_节点树后端->m_节点树绑定的父节点.begin();
		C_节点树* t = 返回的节点->f_get父节点树();

		switch (t->m_树类型) {
		case E_节点树类型::e_type_几何节点树:
		case E_节点树类型::e_type_物体节点树:
		case E_节点树类型::e_type_包节点树:
		case E_节点树类型::e_type_材质节点树:
		case E_节点树类型::e_type_着色节点树:
		case E_节点树类型::e_type_几何着色节点树:
		case E_节点树类型::e_type_渲染节点树:
		case E_节点树类型::e_type_函数节点树:
		case E_节点树类型::e_type_并行节点树:
			f_global_set全局物体显示();
			//f_global_set独立物体显示(t);
			break;
		default:
			f_global_set独立物体显示(t);
			break;
		}

		
		C_节点视口状态条* bar = static_cast<C_节点视口状态条*>(self->m_父部件->m_父部件->m_子组件[2]);
		bar->f_刷新容器显示(t);
		//p.m_PtrItem;
	}

	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_打开返回上级节点面板(C_Widget* self, E_键位执行状态 状态) {
	C_节点视口* view = static_cast<C_节点视口*>(self);

	f_ui_绑定扩展弹出菜单项回调绑定(C_节点视口::g节点层级切换面板, f_用菜单_切换节点视口绑定的节点树);
	f_ui_删除扩展弹出菜单项(C_节点视口::g节点层级切换面板, { -1,-1 });

	g返回菜单以记录节点树.clear();
	f_ui_返回上级菜单项构建(view->m_节点树后端->m_节点树绑定的父节点, 0);

	vec2 pos = C_Widget::g_鼠标.gPos;
	pos.x -= 20;
	pos.x -= 40;
	C_节点视口::g节点层级切换面板->f_setPos(pos);

	f_设置当前操作节点视口(view);
	f_widget_顶层窗口_pop(f_ui_当前窗口管理(), 0);
	f_widget_top_push(f_ui_当前窗口管理(), C_节点视口::g节点层级切换面板);
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_移动节点面板(C_Widget* self, E_键位执行状态 状态) {
	C_节点视口* 节点视口 = static_cast<C_节点视口*>(self);

	std::vector<C_节点基类*> nodes;
	for (auto& panel : 节点视口->m_焦点节点面板) {
		nodes.push_back(panel->m_NodeData);
	}


	auto 初始坐标 = (f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, 节点视口));
	初始坐标 = f_NodePanel_get标准坐标(初始坐标, 节点视口);
	g当前移动面板操作 = new C_节点操作_移动节点(nodes, 初始坐标);


	g移动选中节点 = true;
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_节点添加到框(C_Widget* self, E_键位执行状态 状态) {
	C_节点视口* 节点视口 = static_cast<C_节点视口*>(self);

	auto se = self->f_get更新组件();

	for (auto& 要添加的面板 : 节点视口->m_焦点节点面板) {
		if (要添加的面板->m_NodeData->m_框节点) continue;

		S_Bounding2D bound;
		f_NodePanel_get边界框(要添加的面板, bound);

		

		std::map<int32, C_节点基类*> 背心框节点;
		
		for (uint32 i = 0; i < se.m_Num; ++i) {
			C_节点面板* 框面板 = (C_节点面板*)se.m_W[i];

			if (要添加的面板 == 框面板) continue;
			

			
			switch (框面板->m_NodeData->m_Type){
			case E_节点类型::e_节点Type_框 : {
				if (f_NodePanel_是否已在框内(要添加的面板->m_NodeData, 框面板->m_NodeData)) continue;

				S_Bounding2D 框节点面板包围盒;
				f_NodePanel_get边界框(框面板, 框节点面板包围盒);

				if (f_geom_包围盒碰撞(bound, 框节点面板包围盒)) {
					vec2 size = (框节点面板包围盒.max - 框节点面板包围盒.min);
					背心框节点[int32(size.x * size.y)] = 框面板->m_NodeData;
				}
				break;
			}
			default:
				break;
			}
		}


		if (背心框节点.size()) {
			C_节点基类* 框节点 = (*背心框节点.begin()).second;

			框节点->m_框包含节点.emplace_back(要添加的面板->m_NodeData);
			要添加的面板->m_NodeData->m_框节点 = 框节点;

			for (auto& e : 框节点->m_包含在多个视口的UI部件) {
				uint32 源位置 = f_widget_取组件位置索引(节点视口, 要添加的面板);
				uint32 目标位置 = f_widget_取组件位置索引(节点视口, e);

				f_widget_移动层(节点视口, 源位置, 目标位置);
			}
			
		}
		
		
	}
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_节点相交链接(C_Widget* self, E_键位执行状态 状态) {
	return E_事件是否传递();
}

E_事件是否传递 f_NodeView_节点移动对齐(C_Widget* self, E_键位执行状态 状态) {
	return E_事件是否传递();
}

E_事件是否传递 f_NodeView_节点从框中移出(C_Widget* self, E_键位执行状态 状态) {
	C_节点视口* 节点视口 = static_cast<C_节点视口*>(self);

	for (auto& 面板 : 节点视口->m_焦点节点面板) {
		if (面板->m_NodeData->m_框节点) {
			uint32 id = 0;
			for (auto& e : 面板->m_NodeData->m_框节点->m_框包含节点) {
				if (e == 面板->m_NodeData) {
					面板->m_NodeData->m_框节点->m_框包含节点.erase(面板->m_NodeData->m_框节点->m_框包含节点.begin() + id);
					break;
				}
				++id;
			}
			面板->m_NodeData->m_框节点 = nullptr;
		}
	}
	return E_事件是否传递::e_事件传递_终止;
}







E_事件是否传递 f_NodePlane_进入子集(C_Widget* self, E_键位执行状态 状态) {
	C_节点面板* 节点 = static_cast<C_节点面板*>(self);
	C_节点树* tree = 节点->m_NodeData->f_get子集树();
	if (!tree) return E_事件是否传递::e_事件传递_终止;

	static C_节点树* gThisTree = nullptr;

	switch (tree->m_树类型) {
		case E_节点树类型::e_type_几何节点树:
		case E_节点树类型::e_type_材质节点树:
		case E_节点树类型::e_type_几何着色节点树:
		case E_节点树类型::e_type_着色节点树:
		case E_节点树类型::e_type_物体节点树:
		case E_节点树类型::e_type_函数节点树:
		case E_节点树类型::e_type_并行节点树:
			gThisTree = tree;
			break;

		default:
			if (!gThisTree) gThisTree = 节点->m_NodeData->f_getThis节点树();
			break;
	}

	E_节点树类型 树类型;
	if (gThisTree) {
		树类型 = gThisTree->m_树类型;
	}
	else {
		树类型 = tree->m_树类型;
		tree->f_set父节点(节点->m_NodeData);
	}

	switch (tree->m_树类型) {
		case E_节点树类型::e_type_几何节点树:
		//case E_节点树类型::e_type_材质节点树:
		//case E_节点树类型::e_type_着色节点树:
		//case E_节点树类型::e_type_函数节点树:
			f_global_set独立物体显示(gThisTree);
			break;
		case E_节点树类型::e_type_物体节点树:
			f_global_set全局物体显示();
			break;
	}


	if (节点->m_节点当前所在视口) {
		C_节点视口* view = static_cast<C_节点视口*>(节点->m_节点当前所在视口);
		f_NodeView_绑定节点视口弹出菜单(树类型, view);
		view->f_nodeTree_绑定(tree);


		C_节点视口状态条* bar = static_cast<C_节点视口状态条*>(view->m_父部件->m_父部件->m_子组件[2]);
		bar->f_刷新容器显示(tree);
	}

	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodePlane_添加转接点(C_Widget* self, E_键位执行状态 状态) {
	S_框架::g_开启节点移动 = false;
	return E_事件是否传递();
}




//====================================== 菜单构建和事件函数 ============================================
bool on_NodeView_删除节点菜单项(C_Widget* self, const std::u16string name, uint16 id) {
	if (name == u"删除") {
		std::vector<C_节点面板*> Nodes;
		for (auto& e : g当前操作节点视口->m_焦点节点面板) {
			switch (e->m_NodeData->m_Type) {
				case E_节点类型::e_节点Type_组输入:
				case E_节点类型::e_节点Type_组输出:
					continue;
				default:
					Nodes.push_back(e);
					break;
			}
		}

		if(Nodes.size()){
			g当前删除面板操作 = new C_节点操作_删除节点(g当前操作节点视口, Nodes);
			g当前操作节点视口->m_节点树后端->m_节点结构改变 = true;
		}
	}
	return false;
}

void on_NodeView_设置当前操作视口(C_节点树* t) {
	auto& views = g节点视口[t];

	if (views.size()) {
		f_设置当前操作节点视口(*g节点视口[t].begin());
	}
	else {
		
	}
	
}

void on_NodeView_设置当前操作节点树(C_节点树* t) {

}

C_节点树* f_NodeView_取当前要操作的节点树() {
	C_节点树* 当前操作节点树 = g当前操作节点视口->m_节点树后端;
	if (!当前操作节点树) return nullptr;

	if (g当前节点添加到树_树类型标示 == 0xffffffff) {
		
	}
	else {
		switch (g当前节点添加到树_树类型标示) {
		case E_节点树类型::e_type_物体节点树: {
			当前操作节点树 = S_节点数据::G当前物体节点树;
			break;
		}
		case E_节点树类型::e_type_材质节点树: {
			当前操作节点树 = S_节点数据::G当前材质节点树;
			break;
		}
		case E_节点树类型::e_type_着色节点树: {
			当前操作节点树 = S_节点数据::G当前材质节点树;
			break;
		}
		case E_节点树类型::e_type_渲染节点树: {
			当前操作节点树 = S_节点数据::G当前渲染节点树;
			break;
		}
		case E_节点树类型::e_type_包节点树: {
			当前操作节点树 = S_节点数据::G当前包节点树;
			break;
		}
		}
	}

	return 当前操作节点树;
}

void f_NodeView_设置当前节点添加_树类型限制(uint32 限制类型位标示) {
	g当前节点添加到树_树类型标示 = 限制类型位标示;
}







/************************************
	*        节点回调事件初始化
	*************************************/
void f_NodeView_节点回调事件初始化(C_节点面板* 节点面板) {
	节点面板->m_内部面板->mf_鼠标点击 = f_NodeView_节点面板鼠标单击;

	节点面板->mf_鼠标进入 = on_节点面板鼠标进入;
	节点面板->mf_鼠标离开 = on_节点面板鼠标退出;
}

C_节点面板* f_NodeView_创建节点面板(C_节点基类* node, C_节点视口* view) {
	C_节点面板* plane = 0;

	plane = view->mf_节点重构回调(plane, node);
	plane->f_setOriginalPos(node->m_坐标);
	plane->m_节点当前所在视口 = view;

	f_NodeView_节点回调事件初始化(plane);
	view->f_update节点面板(plane);
	view->f_面板组件添加到视口中(plane);

	f_工作区_联动更新(E_工作区类型::e_节点视口, E_工作区操作类型::e_创建节点);
	return plane;
}

void f_NodeView_置操作节点(C_节点基类* node, C_节点视口* view) {

}

vec2 f_NodeView_置变换后坐标(const vec2& 变换前坐标, S_View属性* view) {
	vec2 pos = (变换前坐标 * view->m_比例) + view->m_中心;
	return pos;
}

vec2 f_NodeView_取变换前坐标(const vec2& 变换后坐标, S_View属性* view) {
	return (变换后坐标 - view->m_中心) / view->m_比例;
}

void f_NodeView_更新前选择节点() {

}




void on_节点树列表_编辑名称(S_Props& prop) {
	auto& p = f_prop_MapPrt(prop);

	p.m_PtrItem;
}

void on_节点树列表_重构节点视口内节点(S_Props& prop) {
	auto& p = f_prop_MapPrt(prop);
}


bool f_NodeView_绑定节点视口弹出菜单(E_节点树类型 节点树类型, C_节点视口* view) {

	switch (节点树类型) {
		case E_节点树类型::e_type_几何节点树:
			view->m_节点创建菜单 = C_节点视口::g多边形节点创建菜单;
			break;

		case E_节点树类型::e_type_物体节点树: {
			view->m_节点创建菜单 = C_节点视口::g物体节点创建菜单;
			break;
		}
		case E_节点树类型::e_type_材质节点树: {
			view->m_节点创建菜单 = C_节点视口::g材质节点创建菜单;
			break;
		}
		case E_节点树类型::e_type_着色节点树: {
			view->m_节点创建菜单 = C_节点视口::g着色器节点创建菜单;
			break;
		}
		case E_节点树类型::e_type_函数节点树:
			view->m_节点创建菜单 = C_节点视口::g函数节点创建菜单;
			break;
		case E_节点树类型::e_type_几何着色节点树:
			view->m_节点创建菜单 = C_节点视口::g几何着色节点创建菜单;
			break;

		case E_节点树类型::e_type_渲染节点树:
			view->m_节点创建菜单 = C_节点视口::g渲染节点创建菜单;
			break;

		case E_节点树类型::e_type_并行节点树:
			view->m_节点创建菜单 = C_节点视口::g并行节点创建菜单;
			break;
		default:
			return false;
	}

	return true;
}



std::vector<C_节点面板*> f_NodeView_从节点添加节点面板到视口(C_节点基类* node, bool 是否清除以前激活节点) {
	if (!g当前操作节点视口->m_节点树后端) return {};

	std::vector<C_节点面板*> 节点面板组;
	for (auto& view : g节点视口[node->f_get父节点树()]) {
		C_节点面板* 节点面板 = view->f_从节点后端创建节点面板(node, 0);


		节点面板->m_节点当前所在视口 = view;
		节点面板->m_父部件 = view;


		/************************************
		*        节点后端数据
		*************************************/
		f_NodeCtx_更新(true);
		f_node_节点树添加节点(view->m_节点树后端, 节点面板->m_NodeData);
		f_NodeCtx_更新完成();

		f_NodeView_节点回调事件初始化(节点面板);
		//节点面板->m_属性面板参数构建(g当前操作节点视口->m_属性栏, (S_结构对象指针)节点面板);


		uint32 id = view->m_子组件.size();
		节点面板->m_实例ID = id;


		
		view->f_update节点面板(节点面板);
		f_NodePanel_鼠标设置节点坐标(节点面板, S_框架::g节点创建初始位置);

		f_widget_单个部件裁剪(节点面板, view->m_裁剪大小, view->m_GlobalLoc);

		节点面板->m_NodeData->f_update();

		std::vector<C_节点面板*> 要添加的面板{ 节点面板 };
		f_NodeView_添加激活节点(要添加的面板, 是否清除以前激活节点);

		节点面板组.push_back(节点面板);
	}

	f_projectFile_设置文件保存状态(false);
	return 节点面板组;
}

//void f_节点构建_拷贝节点(C_节点视口* 节点视口) {
//}


void f_NodeView_设置节点面板局部添加位置(C_节点视口* view, const vec2& 鼠标局部位置, bool 是否中心) {
	//std::cout<<"鼠标局部位置:"<< 鼠标局部位置.x<<" " << 鼠标局部位置.y << std::endl;
	if (view) {
		if (是否中心) {
			S_框架::g节点创建初始位置 = view->m_Size * 0.5 + 鼠标局部位置;
		}
		else {
			S_框架::g节点创建初始位置 = 鼠标局部位置;
		}
	}
	else {
		S_框架::g节点创建初始位置 = 鼠标局部位置;
	}
}

void f_NodeView_更新节点结构() {
	S_Props p = f_alloc_EnumProp(nullptr, {
							{u"中心"},
							{u"边界中心"},
				}, u"计算类型");
	f_prop_enum(p) = 1;

	for (auto& e : g节点添加结构) {
		S_Vec2Array a;
		auto& 交点坐标 = e.second.下层节点坐标;
		a.count = 交点坐标.size();
		a.ptr_userData = 交点坐标.data();
		vec2 中心坐标 = f_mat_点统计v2(&a, &p);


		auto* 转接节点 = f_node_创建插座转接节点(g当前操作节点视口->m_UIctx->m_Ctx, g当前操作节点视口->m_节点树后端);

		f_NodeView_设置节点面板局部添加位置(g当前操作节点视口, 中心坐标, false);
		std::vector<C_节点基类*> 要添加的节点{ 转接节点 };
		f_NodeView_添加节点到视口(g当前操作节点视口, 要添加的节点, true);

		auto* outSocket = e.second.输出插座;
		auto* 上层节点 = outSocket->f_getNode();
		auto* 节点面板 = *上层节点->m_包含在多个视口的UI部件.begin();
		auto* 转接节点面板 = *转接节点->m_包含在多个视口的UI部件.begin();
		f_NodePanel_链接节点((C_节点面板*)节点面板, outSocket->f_get所在节点位置(), (C_节点面板*)转接节点面板, 1);
		转接节点->f_update();


		for (auto& 下层插座 : e.second.下层节点插座) {
			auto* 下层节点 = 下层插座->f_getNode();
			auto* 下层节点面板 = *下层节点->m_包含在多个视口的UI部件.begin();

			f_NodePanel_链接节点((C_节点面板*)转接节点面板, 1, (C_节点面板*)下层节点面板, 下层插座->f_get所在节点位置());
		}
	}

	f_prop_Release(p);

	if (g节点添加结构.size()) {
		g当前操作节点视口->m_更新绘制属性 = true;

		g节点添加结构.clear();
	}
}

void f_NodeView_添加节点面板到视口(C_节点视口* 节点视口, C_节点面板* 节点面板, bool 是否清除以前激活节点) {
	f_NodeView_节点回调事件初始化(节点面板);

	//节点面板->m_NodeData->m_坐标 = S_框架::g节点创建初始位置 / 节点视口->m_扩展视口属性->m_比例;
	节点面板->m_实例ID = 节点视口->m_子组件.size();

	f_NodeCtx_更新(true);
	f_node_节点树添加节点(节点视口->m_节点树后端, 节点面板->m_NodeData);
	f_NodeCtx_更新完成();

	
	C_节点基类* nodeData = 节点面板->m_NodeData;
	for (auto& view : g节点视口[节点面板->m_NodeData->f_get父节点树()]) {
		if (!nodeData) {
			节点面板 = view->f_从节点后端创建节点面板(节点面板->m_NodeData, 0);
		}
		节点面板->m_节点当前所在视口 = view;

		view->f_节点面板添加到节点视口(节点面板, 0);
		nodeData = nullptr;
	}
	

	f_projectFile_设置文件保存状态(false);
	f_widget_单个部件裁剪(节点面板, 节点面板->m_节点当前所在视口->m_裁剪大小, 节点面板->m_节点当前所在视口->m_GlobalLoc);
}

void f_NodeView_其他工作区添加面板到视口(C_Widget* w, bool 是否清除以前激活节点) {
	if(!g当前操作节点视口) return;

	C_节点面板* 节点面板 = dynamic_cast<C_节点面板*>(w);
	C_节点树* 当前操作节点树 = g当前操作节点视口->m_节点树后端;


	if (g当前节点添加到树_树类型标示 == 0xffffffff && 当前操作节点树) {
		f_NodeCtx_更新(true);
		f_node_节点树添加节点(当前操作节点树, 节点面板->m_NodeData);
		f_NodeCtx_更新完成();

		节点面板->m_实例ID = g当前操作节点视口->m_子组件.size();
		g当前操作节点视口->f_update节点面板(节点面板);

	}
	else {
		switch (g当前节点添加到树_树类型标示) {
		case E_节点树类型::e_type_物体节点树 : {
			当前操作节点树 = S_节点数据::G当前物体节点树;
			break;
		}
		case E_节点树类型::e_type_材质节点树: {
			当前操作节点树 = S_节点数据::G当前材质节点树;
			break;
		}
		case E_节点树类型::e_type_着色节点树: {
			当前操作节点树 = S_节点数据::G当前材质节点树;
			break;
		}
		case E_节点树类型::e_type_渲染节点树: {
			当前操作节点树 = S_节点数据::G当前渲染节点树;
			break;
		}
		case E_节点树类型::e_type_包节点树: {
			当前操作节点树 = S_节点数据::G当前包节点树;
			break;
		}
		}
		g当前节点添加到树_树类型标示 = 0xffffffff;

		if (!当前操作节点树) return;
		f_NodeCtx_更新(true);
		f_node_节点树添加节点(当前操作节点树, 节点面板->m_NodeData);
		f_NodeCtx_更新完成();

		if (g节点视口.find(当前操作节点树) == g节点视口.end() || g节点视口[当前操作节点树].size() == 0) {
			节点面板->m_是否已弃用 = true;
			return;
		}
	}
	if (!当前操作节点树) return;

	

	C_节点基类* nodeData = 节点面板->m_NodeData;
	if (g节点视口.find(当前操作节点树) != g节点视口.end()) {
		for (auto& view : g节点视口[当前操作节点树]) {
			if (!nodeData) {
				节点面板 = view->f_从节点后端创建节点面板(节点面板->m_NodeData, 0);
			}
			节点面板->m_节点当前所在视口 = view;
			//节点面板->m_NodeData->m_坐标


			view->f_节点面板添加到节点视口(节点面板, 0);
			f_节点缩放(view);
			nodeData = nullptr;
		}
	}
	else {
		节点面板->m_节点当前所在视口 = g当前操作节点视口;
		g当前操作节点视口->f_节点面板添加到节点视口(节点面板, 0);
	}
	

	f_NodePanel_鼠标设置节点坐标(节点面板, S_框架::g节点创建初始位置);
	
	std::vector<C_节点面板*> 要添加的面板{ 节点面板 };
	f_NodeView_添加激活节点(要添加的面板, 是否清除以前激活节点);
	f_widget_单个部件裁剪(节点面板, 节点面板->m_节点当前所在视口->m_裁剪大小, 节点面板->m_节点当前所在视口->m_GlobalLoc);

	f_projectFile_设置文件保存状态(false);
	g当前节点添加到树_树类型标示 = 0xffffffff;

	f_工作区_联动更新(E_工作区类型::e_节点视口, E_工作区操作类型::e_创建节点);
}


void f_NodeView_添加节点面板到视口(std::vector<C_节点面板*>& ws, bool 是否清除以前激活节点) {
	if (是否清除以前激活节点) {
		g当前操作节点视口->f_清除所有节点面板();
	}
	
	for (auto& 节点面板 : ws) {
		f_NodeView_添加节点面板到视口(g当前操作节点视口, 节点面板, false);
		f_NodePanel_鼠标设置节点坐标(节点面板, S_框架::g节点创建初始位置);
		f_widget_单个部件裁剪(节点面板, 节点面板->m_节点当前所在视口->m_裁剪大小, 节点面板->m_节点当前所在视口->m_GlobalLoc);
	}

	f_NodeView_添加激活节点(ws, true);
	f_projectFile_设置文件保存状态(false);
}

void f_NodeView_添加节点到视口(C_节点视口* 节点视口, std::vector<C_节点基类*>& ws, bool 是否清除以前激活节点) {
	g当前操作节点视口 = 节点视口;
	C_节点树* 当前操作节点树 = f_NodeView_取当前要操作的节点树();

	f_NodeCtx_更新(true);
	for (auto& e : ws) {
		f_node_节点树添加节点(当前操作节点树, e);

		for (auto& view : g节点视口[当前操作节点树]) {
			C_节点面板* 节点面板 = 节点视口->f_从节点后端创建节点面板(e, 0);

			//节点面板->f_绑定绘制属性(view->m_视口画布属性);
			节点面板->m_节点当前所在视口 = view;
			//view->f_节点面板添加到节点视口(节点面板, 0);
			
			f_NodePanel_鼠标设置节点坐标(节点面板, S_框架::g节点创建初始位置 - f_NodePanel_get中心偏移(节点面板) * 节点视口->m_扩展视口属性->m_比例);
			f_widget_单个部件裁剪(节点面板, 节点面板->m_节点当前所在视口->m_裁剪大小, 节点面板->m_节点当前所在视口->m_GlobalLoc);
		}
	}
	f_NodeCtx_更新完成();

	f_projectFile_设置文件保存状态(false);
}





//============================================================ 撤销重做操作函数实现 ==========================================================================================
uint8 C_节点操作_添加节点::f_撤销() {
	return C_操作记录::f_撤销();
}

uint8 C_节点操作_添加节点::f_重做() {
	return C_操作记录::f_重做();
}




C_节点操作_删除节点::C_节点操作_删除节点(C_节点视口* View, const std::vector<C_节点面板*>& Nodes) : m_View(nullptr), C_操作记录(E_编辑操作类型::e_操作类型_删除节点) {
	for (auto& e : Nodes) {
		if(e->m_NodeData->m_冻结数据 || e->m_NodeData->m_越过更新) return;
	}
	m_节点面板 = Nodes;
	m_View = View;
	f_重做();
}

C_节点操作_删除节点::~C_节点操作_删除节点() {
	for (auto& e : m_节点面板) {

	}
}

uint8 C_节点操作_删除节点::f_撤销() {
	if(!m_View) return C_操作记录::f_撤销();

	f_设置当前操作节点视口(m_View);

	for (auto& e : m_节点面板) {
		S_框架::g节点创建初始位置 = e->f_getPos();

		e->m_NodeData->f_回收();
		m_View->mf_节点重构回调(e, e->m_NodeData);

		f_NodeView_其他工作区添加面板到视口(e, false);
		e->m_NodeData->m_包含在多个视口的UI部件.insert(e);

		for (auto& link : m_断开的连接线) {
			link->f_Link(link->m_插座in, link->m_插座out);
		}
	}

	f_NodeView_添加激活节点(m_节点面板, true);

	m_View->m_属性栏->f_构建属性部件({});
	m_View->m_更新绘制属性 = true;
	return C_操作记录::f_撤销();
}

uint8 C_节点操作_删除节点::f_重做() {
	if(!m_View) return C_操作记录::f_重做();

	f_设置当前操作节点视口(m_View);
	f_NodeCtx_更新(true);

	for (auto& e : m_节点面板) {
		e->m_NodeData->f_删除();
		e->m_NodeData->m_区域内节点.clear();

		for (auto& socket : e->f_getInSocket()) {
			for (auto& link : socket->m_SocketData->f_getLinkLines()) {
				link->f_断开输出链接();
				m_断开的连接线.push_back(link);
			}
		}

		for (auto& socket : e->f_getOutSocket()) {
			for (auto& link : socket->m_SocketData->f_getLinkLines()) {
				link->f_断开输出链接();
				m_断开的连接线.push_back(link);
			}
		}


		if (e->m_NodeData->m_框节点) {
			for (auto it = e->m_NodeData->m_框节点->m_框包含节点.begin(); it != e->m_NodeData->m_框节点->m_框包含节点.end(); ++it) {
				if ((*it) == e->m_NodeData) {
					e->m_NodeData->m_框节点->m_框包含节点.erase(it);
					break;
				}
			}
		}


		f_node_节点树删除节点(static_cast<C_节点视口*>(e->m_节点当前所在视口)->m_节点树后端, e->m_NodeData);
		auto uis = e->m_NodeData->m_包含在多个视口的UI部件;
		for (auto& plane : uis) {
			auto nodePlane = static_cast<C_节点面板*>(plane);
			
			static_cast<C_节点视口*>(nodePlane->m_节点当前所在视口)->f_清除所有节点面板();
			std::vector<C_Widget*> 节点面板组 = { nodePlane };
			f_widget_remove子部件(static_cast<C_节点视口*>(nodePlane->m_节点当前所在视口), 节点面板组, false);
			//}
			static_cast<C_节点视口*>(nodePlane->m_节点当前所在视口)->f_节点面板从节点视口移入回收站(nodePlane);
		}

	}
	
	
	m_View->m_属性栏->f_构建属性部件({});
	m_View->m_更新绘制属性 = true;

	f_NodeCtx_更新完成();
	f_NodeCtx_clear选中编辑物体();

	return C_操作记录::f_重做();
}






uint8 C_节点操作_选择节点::f_撤销() {
	return C_操作记录::f_撤销();
}

uint8 C_节点操作_选择节点::f_重做() {
	return C_操作记录::f_重做();
}

void C_节点操作_选择节点::f_记录() {

}





S_关联拷贝节点剪切板::S_关联拷贝节点剪切板(std::set<C_节点面板*>& nodes) {
	m_要拷贝的节点面板.reserve(nodes.size());

	for (auto& e : nodes) {
		m_要拷贝的节点面板.push_back(e->m_NodeData);
	}
}

void S_关联拷贝节点剪切板::f_复制() {
	uint32 num = m_要拷贝的节点面板.size();
	m_节点中心坐标偏移.resize(num);
	vec2* 中心偏移 = m_节点中心坐标偏移.data();

	vec2 中心坐标 = {};

	for (uint32 i = 0; i < num; ++i) {
		中心坐标 += m_要拷贝的节点面板[i]->m_坐标;
	}
	中心坐标 /= num;

	for (uint32 i = 0; i < num; ++i) {
		中心偏移[i] = 中心坐标 - m_要拷贝的节点面板[i]->m_坐标;
	}
}

void S_关联拷贝节点剪切板::f_粘贴() {
	std::vector<C_节点面板*> 新节点面板 = f_NodeView_拷贝节点(g当前操作节点视口, m_要拷贝的节点面板, false);

	auto 鼠标坐标 = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, g当前操作节点视口);
	鼠标坐标 = f_NodeView_取变换前坐标(鼠标坐标, g当前操作节点视口->m_扩展视口属性);

	vec2* 中心偏移 = m_节点中心坐标偏移.data();

	uint32 i = 0;
	for (auto& e : 新节点面板) {
		e->m_NodeData->m_坐标 = 鼠标坐标 - 中心偏移[i];
		e->m_NodeData->m_坐标 -= f_NodePanel_get中心偏移(e);
		f_NodePanel_更新布局(e);
		++i;
	}

	uint32 num = m_要拷贝的节点面板.size();
	for (uint32 i = 0; i < num; ++i) {
		for (auto& nodePanel : m_要拷贝的节点面板[i]->m_包含在多个视口的UI部件) {
			新节点面板[i]->m_同子集节点树关联UI部件 = ((C_节点面板*)nodePanel)->m_同子集节点树关联UI部件;
		}
		if(新节点面板[i]->m_同子集节点树关联UI部件) (*新节点面板[i]->m_同子集节点树关联UI部件).insert(新节点面板[i]);
	}
	
}


S_深拷贝节点剪切板::S_深拷贝节点剪切板(std::set<C_节点面板*>& nodes) {
	m_要拷贝的节点面板.reserve(nodes.size());

	for (auto& e : nodes) {
		m_要拷贝的节点面板.push_back(e->m_NodeData);
	}
}

void S_深拷贝节点剪切板::f_复制() {
	uint32 num = m_要拷贝的节点面板.size();
	m_节点中心坐标偏移.resize(num);
	vec2* 中心偏移 = m_节点中心坐标偏移.data();

	vec2 中心坐标 = {};

	for (uint32 i = 0; i < num; ++i) {
		中心坐标 += m_要拷贝的节点面板[i]->m_坐标;
	}
	中心坐标 /= num;

	for (uint32 i = 0; i < num; ++i) {
		中心偏移[i] = 中心坐标 - m_要拷贝的节点面板[i]->m_坐标;
	}
}

void S_深拷贝节点剪切板::f_粘贴() {
	auto 新节点面板 = f_NodeView_拷贝节点(g当前操作节点视口, m_要拷贝的节点面板, true);

	auto 鼠标坐标 = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, g当前操作节点视口);
	鼠标坐标 = f_NodeView_取变换前坐标(鼠标坐标, g当前操作节点视口->m_扩展视口属性);

	vec2* 中心偏移 = m_节点中心坐标偏移.data();

	uint32 i = 0;
	for (auto& e : 新节点面板) {
		e->m_NodeData->m_坐标 = 鼠标坐标 - 中心偏移[i];
		e->m_NodeData->m_坐标 -= f_NodePanel_get中心偏移(e);
		f_NodePanel_更新布局(e);
		++i;
	}

	uint32 num = m_要拷贝的节点面板.size();
	for (uint32 i = 0; i < num; ++i) {
		for (auto& nodePanel : m_要拷贝的节点面板[i]->m_包含在多个视口的UI部件) {
			新节点面板[i]->m_同子集节点树关联UI部件 = ((C_节点面板*)nodePanel)->m_同子集节点树关联UI部件;
		}
		if (新节点面板[i]->m_同子集节点树关联UI部件) (*新节点面板[i]->m_同子集节点树关联UI部件).insert(新节点面板[i]);
	}
}







static E_事件是否传递 on_滚动框鼠标准备拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	//C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);
	vec2 mousePos = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
	//g选框起点坐标 = (mousePos - 节点视口->m_扩展视口属性->m_中心) / 节点视口->m_扩展视口属性->m_比例;
	return E_事件是否传递::e_事件传递_继续;
}

static E_事件是否传递 on_滚动框准备拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	//C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);

	return E_事件是否传递::e_事件传递_终止;
}

static bool on_滚动框鼠标结束拖动(C_Widget* self, C_Widget* 拾取的部件, S_拖放数据& data) {
	return true;
}



static E_事件是否传递 on_节点滚动框鼠标按下(C_Widget* self, const E_鼠标按钮& 按钮) {
	if (按钮 == E_鼠标按钮::e_鼠标中键) {
		g开始移动视口 = true;
	}
	return E_事件是否传递::e_事件传递_终止;
}
static E_事件是否传递 on_节点滚动框鼠标放开(C_Widget* self, const E_鼠标按钮& 按钮) {
	if (按钮 == E_鼠标按钮::e_鼠标中键) {
		g开始移动视口 = false;
	}
	return E_事件是否传递::e_事件传递_终止;
}
static E_事件是否传递 on_节点滚动框鼠标位置改变(C_Widget* self, const ivec4& mouse) {
	
	if (g开始移动视口) {
		auto view = self->m_子组件[0];
		view->f_setPos(view->m_Pos + C_Widget::g_鼠标.abs);
		f_widget_更新绘制(view);
	}
	return E_事件是否传递::e_事件传递_终止;
}
static E_事件是否传递 on_节点滚动框鼠标滚轮(C_Widget* self, const S_鼠标& 鼠标) {
	C_节点视口& 节点视口 = *static_cast<C_节点视口*>(self->m_子组件[0]);
	
	float32 z = C_Widget::g_鼠标.lZ;
	vec2 s = { 1,1 };
	if (z > 0) {
		s.x = 1.15;
		s.y = 1.15;
	}
	else if (z < 0) {
		s.x = 0.9;
		s.y = 0.9;
	}

	f_Widget_鼠标位置视口缩放(&节点视口, s);
	f_widget_更新绘制(&节点视口);
	节点视口.f_Scale();

	f_projectFile_设置文件保存状态(false);
	return E_事件是否传递::e_事件传递_终止;
}




static void on_节点滚动视口布局(C_Widget* self, C_Widget** w, uint32 num) {
	//w[0]->f_setSize(self->m_Size);
	w[0]->m_更新绘制属性 = true;

	w[1]->f_setW(self->m_Size.x);

	//f_Widget_保持偏移比例(self, w[0]);
}
static void on_节点滚动视口绘制(C_Widget* self, S_2D画布* 画布) {
	
	auto 背景 = f_vg_genConvex((*self), *画布, E_填充模式::e_填充面, E_图层混合模式::e_Normal);
	auto 刻度 = f_vg_genConvex((*self), *画布, E_填充模式::e_填充面, E_图层混合模式::e_Normal);

	return;
}
static void on_节点滚动视口变换(C_Widget* self) {
	S_Surface2D& 背景 = *f_widget_getSurface2D(self, 0);

	std::vector<S_2DConvexRect>	区域(1);
	std::vector<S_GPU内存块*>	形状(1);
	std::vector<S_2D颜色包>		颜色(1);
	std::vector<vec2>			位置(1);

	S_2DConvexRect rect{};
	rect.size = self->m_Size;
	区域[0] = rect;
	形状[0] = &self->m_UIctx->m_Ctx.m_几何图形->m_平面矩形;
	颜色[0] = S_2D颜色包({50,50,50,255}, 0, 0, 0);
	位置[0] = {};

	f_vg_drawConvex(背景, 1, 形状.data(), 区域.data());
	f_vg_tranform(背景, 1, 位置.data());
	f_vg_color(背景, 1, 颜色.data());

}


static void on_搭线面板布局(C_Widget* self, C_Widget** w, uint32 num) {
	//w[0]->m_更新绘制属性 = true;


}
static void on_搭线面板绘制(C_Widget* self, S_2D画布* 画布) {
	auto 背景 = f_vg_genConvex((*self), *画布, E_填充模式::e_填充面, E_图层混合模式::e_Normal);
	//auto 刻度 = f_vg_genConvex((*self), *画布, E_填充模式::e_填充面, E_图层混合模式::e_Normal);

	return;
}
static void on_搭线面板变换(C_Widget* self) {
	S_Surface2D& 背景 = *f_widget_getSurface2D(self, 0);

	std::vector<S_2DConvexRect>	区域(1);
	std::vector<S_GPU内存块*>	形状(1);
	std::vector<S_2D颜色包>		颜色(1);
	std::vector<vec2>			位置(1);

	S_2DConvexRect rect{};
	rect.size = self->m_Size;
	区域[0] = rect;
	形状[0] = &self->m_UIctx->m_Ctx.m_几何图形->m_平面矩形;
	颜色[0] = S_2D颜色包({200,200,200,150}, 255, 0, 0);
	位置[0] = {};

	f_vg_drawConvex(背景, 1, 形状.data(), 区域.data());
	f_vg_tranform(背景, 1, 位置.data());
	f_vg_color(背景, 1, 颜色.data());

}

C_Widget* f_NodeView_创建节点视口(S_UI渲染环境& ctx, C_节点树* tree) {
	auto* 节点视口 = new C_节点视口(ctx, tree);
	f_widget_开启视口属性(节点视口);

	C_Widget* 搭线面板 = new C_Widget(&ctx);
	搭线面板->mf_布局 = on_搭线面板布局;
	搭线面板->mf_绘制 = on_搭线面板绘制;
	搭线面板->mf_变换 = on_搭线面板变换;
	搭线面板->f_setMinSize({100,100});
	搭线面板->m_WName = u"搭线面板";
	搭线面板->m_TypeName = "C_Widget 搭线面板";
	f_widget_添加绘制画布(搭线面板);
	
	S_框架::m_工作区属性面板渲染层ID分配.f_置组件渲染层(搭线面板);



	节点视口->m_属性栏 = f_创建属性边栏(ctx);
	节点视口->m_属性栏->f_setSize({ 256,256 });
	
	节点视口->m_扩展视口属性->m_缩放后边界 = 节点视口->f_get边界();
	节点视口->m_扩展视口属性->m_偏移比例 = { 0.5f, 0.5f };
	节点视口->m_扩展视口属性->m_中心 = { 400, 400 };
	节点视口->m_扩展视口属性->m_比例 = { 1, 1 };
	节点视口->m_扩展视口属性->m_偏移比例 = { 0,0 };
	
	节点视口->m_节点树后端 = tree;
	节点视口->m_节点创建菜单 = C_节点视口::g物体节点创建菜单;
	节点视口->m_节点删除菜单 = f_通用菜单_节点删除菜单(ctx, on_NodeView_删除节点菜单项);

	节点视口->mf_以名称构建节点 = f_节点构建_以名称构建节点;
	节点视口->mf_以节点构建节点 = f_节点构建_从节点构建节点;
	节点视口->mf_节点重构回调 = f_节点构建_重构;
	节点视口->m_使用父组件裁剪 = true;


	C_Widget* 滚动窗口 = new C_Widget(&ctx);
	滚动窗口->mf_鼠标滚轮 = nullptr;
	滚动窗口->mf_布局 = on_节点滚动视口布局;
	滚动窗口->mf_绘制 = on_节点滚动视口绘制;
	滚动窗口->mf_变换 = on_节点滚动视口变换;

	滚动窗口->mf_开始拖拽	= on_滚动框鼠标准备拖动;
	滚动窗口->mf_拖拽		= on_滚动框准备拖动;
	滚动窗口->mf_结束拖拽	= on_滚动框鼠标结束拖动;

	滚动窗口->mf_鼠标按键按下	= on_节点滚动框鼠标按下;
	滚动窗口->mf_鼠标按键放开	= on_节点滚动框鼠标放开;
	滚动窗口->mf_鼠标移动		= on_节点滚动框鼠标位置改变;
	滚动窗口->mf_鼠标滚轮		= on_节点滚动框鼠标滚轮;
	f_widget_添加绘制画布(滚动窗口);
	

	滚动窗口->f_添加子组件(节点视口);
	滚动窗口->f_添加子组件(搭线面板);
	//S_框架::m_工作区视口渲染层ID分配.f_置组件渲染层(滚动窗口);


	S_键盘键位映射* 快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_深度拷贝节点;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_左SHIFT;
	快捷键->键盘状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[1] = S_InputKey::e_KEY_左ALT;
	快捷键->键盘状态[1] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[2] = S_InputKey::e_KEY_D;
	快捷键->键盘状态[2] = DEF_按键状态_按下;
	C_键位映射配置::f_添加节点视口操作键位(滚动窗口, (S_键位映射*)快捷键, u"深度复制节点", u"深度复制节点");

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_关联拷贝节点;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_左SHIFT;
	快捷键->键盘状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[1] = S_InputKey::e_KEY_D;
	快捷键->键盘状态[1] = DEF_按键状态_按下;
	C_键位映射配置::f_添加节点视口操作键位(滚动窗口, (S_键位映射*)快捷键, u"复制节点", u"复制节点");

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_浅拷贝节点到剪切板;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_左CTRL;
	快捷键->键盘状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[1] = S_InputKey::e_KEY_C;
	快捷键->键盘状态[1] = DEF_按键状态_按下;
	C_键位映射配置::f_添加节点视口操作键位(滚动窗口, (S_键位映射*)快捷键, u"拷贝节点", u"拷贝节点");

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_从剪切板粘贴拷拷贝节点;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_左CTRL;
	快捷键->键盘状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[1] = S_InputKey::e_KEY_V;
	快捷键->键盘状态[1] = DEF_按键状态_按下;
	C_键位映射配置::f_添加节点视口操作键位(滚动窗口, (S_键位映射*)快捷键, u"粘贴节点", u"粘贴节点");

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_深拷贝节点到剪切板;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_左SHIFT;
	快捷键->键盘状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[1] = S_InputKey::e_KEY_C;
	快捷键->键盘状态[1] = DEF_按键状态_按下;
	f_ui_注册键盘快捷键(滚动窗口, 快捷键);

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_从剪切板粘贴深拷贝节点;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_左SHIFT;
	快捷键->键盘状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[1] = S_InputKey::e_KEY_V;
	快捷键->键盘状态[1] = DEF_按键状态_按下;
	f_ui_注册键盘快捷键(滚动窗口, 快捷键);



	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_快捷键_返回上级节点;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_退格;
	快捷键->键盘状态[0] = DEF_按键状态_按下;
	C_键位映射配置::f_添加节点视口操作键位(滚动窗口, (S_键位映射*)快捷键, u"返回上级", u"返回上级");

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_节点从框中移出;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_P;
	快捷键->键盘状态[0] = DEF_按键状态_按下;
	C_键位映射配置::f_添加节点视口操作键位(滚动窗口, (S_键位映射*)快捷键, u"从框中移出", u"从框中移出");

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_移动节点面板;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_G;
	快捷键->键盘状态[0] = DEF_按键状态_按下;
	C_键位映射配置::f_添加节点视口操作键位(滚动窗口, (S_键位映射*)快捷键, u"移动节点", u"移动节点");

	S_鼠标双击键位映射* 鼠标双击键位 = f_widget_alloc_鼠标双击键位映射();
	鼠标双击键位->event = f_NodeView_打开返回上级节点面板;
	鼠标双击键位->鼠标按键[0] = E_Input_鼠标按键::e_右键;
	鼠标双击键位->鼠标状态[0] = DEF_按键状态_按下;
	鼠标双击键位->发生组件 = 滚动窗口;
	C_键位映射配置::f_添加节点视口操作键位(滚动窗口, (S_键位映射*)快捷键, u"弹出返回上级菜单", u"弹出返回上级菜单");


	S_拖拽键位映射* 拖放键位;
	拖放键位 = f_widget_init_拖放键位映射();
	拖放键位->event = f_NodePlane_添加转接点;
	拖放键位->键盘按键[0] = S_InputKey::e_KEY_左CTRL;
	拖放键位->键盘状态[0] = DEF_按键状态_按下中;
	拖放键位->鼠标按键[0] = E_Input_鼠标按键::e_左键;
	拖放键位->鼠标状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	拖放键位->发生组件 = 滚动窗口;
	C_键位映射配置::f_添加节点视口操作键位(滚动窗口, (S_键位映射*)拖放键位, u"添加转接点", u"添加转接点");


	f_设置当前操作节点视口(节点视口);
	return 滚动窗口;
}











